sources/oauth: migrate to webcomponents
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
parent
a085632b8e
commit
533a719914
|
@ -1,42 +0,0 @@
|
||||||
{% extends "administration/base.html" %}
|
|
||||||
|
|
||||||
{% load i18n %}
|
|
||||||
{% load authentik_utils %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<section class="pf-c-page__main-section pf-m-light">
|
|
||||||
<div class="pf-c-content">
|
|
||||||
{% block above_form %}
|
|
||||||
<h1>
|
|
||||||
{% blocktrans with object_type=object|verbose_name %}
|
|
||||||
Disable {{ object_type }}
|
|
||||||
{% endblocktrans %}
|
|
||||||
</h1>
|
|
||||||
{% endblock %}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
<section class="pf-c-page__main-section">
|
|
||||||
<div class="pf-l-stack">
|
|
||||||
<div class="pf-l-stack__item">
|
|
||||||
<div class="pf-c-card">
|
|
||||||
<div class="pf-c-card__body">
|
|
||||||
<form action="" method="post" class="pf-c-form">
|
|
||||||
{% csrf_token %}
|
|
||||||
<p>
|
|
||||||
{% blocktrans with object_type=object|verbose_name name=object %}
|
|
||||||
Are you sure you want to disable {{ object_type }} "{{ object }}"?
|
|
||||||
{% endblocktrans %}
|
|
||||||
</p>
|
|
||||||
<div class="pf-c-form__group pf-m-action">
|
|
||||||
<div class="pf-c-form__actions">
|
|
||||||
<input class="pf-c-button pf-m-danger" type="submit" value="{% trans 'Disable' %}" />
|
|
||||||
<a class="pf-c-button pf-m-secondary" href="{% back %}">{% trans "Back" %}</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
{% endblock %}
|
|
|
@ -30,7 +30,7 @@
|
||||||
<ak-spinner-button form="main-form">
|
<ak-spinner-button form="main-form">
|
||||||
{% block action %}{% endblock %}
|
{% block action %}{% endblock %}
|
||||||
</ak-spinner-button>
|
</ak-spinner-button>
|
||||||
<a class="pf-c-button pf-m-secondary" href="{% back %}">{% trans "Cancel" %}</a>
|
<a class="pf-c-button pf-m-secondary" href="#/">{% trans "Cancel" %}</a>
|
||||||
</footer>
|
</footer>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,10 @@
|
||||||
|
|
||||||
{% load static %}
|
{% load static %}
|
||||||
|
|
||||||
|
{% block title %}
|
||||||
|
authentik API Browser
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block head %}
|
{% block head %}
|
||||||
<script type="module" src="{% static 'dist/rapidoc-min.js' %}"></script>
|
<script type="module" src="{% static 'dist/rapidoc-min.js' %}"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -107,7 +107,6 @@ router.register("core/applications", ApplicationViewSet)
|
||||||
router.register("core/groups", GroupViewSet)
|
router.register("core/groups", GroupViewSet)
|
||||||
router.register("core/users", UserViewSet)
|
router.register("core/users", UserViewSet)
|
||||||
router.register("core/user_consent", UserConsentViewSet)
|
router.register("core/user_consent", UserConsentViewSet)
|
||||||
router.register("core/source_user_connections_oauth", UserOAuthSourceConnectionViewSet)
|
|
||||||
router.register("core/tokens", TokenViewSet)
|
router.register("core/tokens", TokenViewSet)
|
||||||
|
|
||||||
router.register("outposts/outposts", OutpostViewSet)
|
router.register("outposts/outposts", OutpostViewSet)
|
||||||
|
@ -129,6 +128,7 @@ router.register("events/transports", NotificationTransportViewSet)
|
||||||
router.register("events/rules", NotificationRuleViewSet)
|
router.register("events/rules", NotificationRuleViewSet)
|
||||||
|
|
||||||
router.register("sources/all", SourceViewSet)
|
router.register("sources/all", SourceViewSet)
|
||||||
|
router.register("sources/oauth_user_connections", UserOAuthSourceConnectionViewSet)
|
||||||
router.register("sources/ldap", LDAPSourceViewSet)
|
router.register("sources/ldap", LDAPSourceViewSet)
|
||||||
router.register("sources/saml", SAMLSourceViewSet)
|
router.register("sources/saml", SAMLSourceViewSet)
|
||||||
router.register("sources/oauth", OAuthSourceViewSet)
|
router.register("sources/oauth", OAuthSourceViewSet)
|
||||||
|
|
|
@ -94,7 +94,7 @@ class SourceViewSet(
|
||||||
if not policy_engine.passing:
|
if not policy_engine.passing:
|
||||||
continue
|
continue
|
||||||
source_settings = source.ui_user_settings
|
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():
|
if not source_settings.is_valid():
|
||||||
LOGGER.warning(source_settings.errors)
|
LOGGER.warning(source_settings.errors)
|
||||||
matching_sources.append(source_settings.validated_data)
|
matching_sources.append(source_settings.validated_data)
|
||||||
|
|
|
@ -25,9 +25,6 @@
|
||||||
<h3>{% trans message %}</h3>
|
<h3>{% trans message %}</h3>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% if 'back' in request.GET %}
|
|
||||||
<a href="{% back %}" class="pf-c-button pf-m-primary pf-m-block">{% trans 'Back' %}</a>
|
|
||||||
{% endif %}
|
|
||||||
<a href="/" class="pf-c-button pf-m-primary pf-m-block">{% trans 'Go to home' %}</a>
|
<a href="/" class="pf-c-button pf-m-primary pf-m-block">{% trans 'Go to home' %}</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,37 +0,0 @@
|
||||||
{% load i18n %}
|
|
||||||
{% load authentik_utils %}
|
|
||||||
|
|
||||||
<section class="pf-c-page__main-section pf-m-light">
|
|
||||||
<div class="pf-c-content">
|
|
||||||
{% block above_form %}
|
|
||||||
<h1>
|
|
||||||
{% blocktrans with object_type=object|verbose_name %}
|
|
||||||
Delete {{ object_type }}
|
|
||||||
{% endblocktrans %}
|
|
||||||
</h1>
|
|
||||||
{% endblock %}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
<section class="pf-c-page__main-section">
|
|
||||||
<div class="pf-l-stack">
|
|
||||||
<div class="pf-l-stack__item">
|
|
||||||
<div class="pf-c-card">
|
|
||||||
<div class="pf-c-card__body">
|
|
||||||
<form id="delete-form" action="" method="post" class="pf-c-form">
|
|
||||||
{% csrf_token %}
|
|
||||||
<p>
|
|
||||||
{% blocktrans with object_type=object|verbose_name name=object %}
|
|
||||||
Are you sure you want to delete {{ object_type }} "{{ object }}"?
|
|
||||||
{% endblocktrans %}
|
|
||||||
</p>
|
|
||||||
<input type="hidden" name="confirmdelete" value="yes">
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
<footer class="pf-c-modal-box__footer">
|
|
||||||
<input class="pf-c-button pf-m-danger" type="submit" form="delete-form" value="{% trans 'Delete' %}" />
|
|
||||||
<a class="pf-c-button pf-m-secondary" href="{% back %}">{% trans "Back" %}</a>
|
|
||||||
</footer>
|
|
|
@ -1,26 +0,0 @@
|
||||||
{% load static %}
|
|
||||||
{% load i18n %}
|
|
||||||
|
|
||||||
<ak-message-container></ak-message-container>
|
|
||||||
|
|
||||||
<header class="pf-c-login__main-header">
|
|
||||||
<h1 class="pf-c-title pf-m-3xl">
|
|
||||||
{% block card_title %}
|
|
||||||
{% trans title %}
|
|
||||||
{% endblock %}
|
|
||||||
</h1>
|
|
||||||
</header>
|
|
||||||
<div class="pf-c-login__main-body">
|
|
||||||
{% block card %}
|
|
||||||
<form method="POST" class="pf-c-form">
|
|
||||||
{% include 'partials/form.html' %}
|
|
||||||
<div class="pf-c-form__group pf-m-action">
|
|
||||||
<button class="pf-c-button pf-m-primary pf-m-block" type="submit">Log in</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
{% endblock %}
|
|
||||||
</div>
|
|
||||||
<footer class="pf-c-login__main-footer">
|
|
||||||
<ul class="pf-c-login__main-footer-links">
|
|
||||||
</ul>
|
|
||||||
</footer>
|
|
|
@ -1,73 +0,0 @@
|
||||||
{% load authentik_utils %}
|
|
||||||
{% load i18n %}
|
|
||||||
|
|
||||||
{% csrf_token %}
|
|
||||||
{% if form.non_field_errors %}
|
|
||||||
<div class="pf-c-form__group">
|
|
||||||
<p class="pf-c-form__helper-text pf-m-error">
|
|
||||||
{{ form.non_field_errors }}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
{% for field in form %}
|
|
||||||
{% if field.field.widget|fieldtype == 'HiddenInput' %}
|
|
||||||
{{ field }}
|
|
||||||
{% else %}
|
|
||||||
<div class="pf-c-form__group">
|
|
||||||
{% if field.field.widget|fieldtype == 'RadioSelect' %}
|
|
||||||
<label class="pf-c-form__label" {% if field.field.required %}class="required" {% endif %}
|
|
||||||
for="{{ field.name }}-{{ forloop.counter0 }}">
|
|
||||||
{{ field.label }}
|
|
||||||
</label>
|
|
||||||
{% for c in field %}
|
|
||||||
<div class="radio col-sm-10">
|
|
||||||
<input type="radio" id="{{ field.name }}-{{ forloop.counter0 }}"
|
|
||||||
name="{% if wizard %}{{ wizard.steps.current }}-{% endif %}{{ field.name }}" value="{{ c.data.value }}"
|
|
||||||
{% if c.data.selected %} checked {% endif %}>
|
|
||||||
<label class="pf-c-form__label" for="{{ field.name }}-{{ forloop.counter0 }}">{{ c.choice_label }}</label>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
{% elif field.field.widget|fieldtype == 'Select' %}
|
|
||||||
<label class="pf-c-form__label" {% if field.field.required %}class="required" {% endif %}
|
|
||||||
for="{{ field.name }}-{{ forloop.counter0 }}">
|
|
||||||
{{ field.label }}
|
|
||||||
</label>
|
|
||||||
<div class="select col-sm-10">
|
|
||||||
{{ field }}
|
|
||||||
</div>
|
|
||||||
{% if field.help_text %}
|
|
||||||
<span>
|
|
||||||
{{ field.help_text }}
|
|
||||||
</span>
|
|
||||||
{% endif %}
|
|
||||||
{% elif field.field.widget|fieldtype == 'CheckboxInput' %}
|
|
||||||
<label class="checkbox-label">
|
|
||||||
{{ field }} {{ field.label }}
|
|
||||||
</label>
|
|
||||||
{% if field.help_text %}
|
|
||||||
<span>
|
|
||||||
{{ field.help_text }}
|
|
||||||
</span>
|
|
||||||
{% endif %}
|
|
||||||
{% else %}
|
|
||||||
<label class="pf-c-form__label" for="{{ field.name }}-{{ forloop.counter0 }}">
|
|
||||||
<span class="pf-c-form__label-text">{{ field.label }}</span>
|
|
||||||
{% if field.field.required %}
|
|
||||||
<span class="pf-c-form__label-required" aria-hidden="true">*</span>
|
|
||||||
{% endif %}
|
|
||||||
</label>
|
|
||||||
{{ field|css_class:'pf-c-form-control' }}
|
|
||||||
{% if field.help_text %}
|
|
||||||
<span>
|
|
||||||
{{ field.help_text }}
|
|
||||||
</span>
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
{% for error in field.errors %}
|
|
||||||
<p class="pf-c-form__helper-text pf-m-error">
|
|
||||||
{{ error }}
|
|
||||||
</p>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
|
@ -87,7 +87,7 @@ class StageViewSet(
|
||||||
user_settings = stage.ui_user_settings
|
user_settings = stage.ui_user_settings
|
||||||
if not user_settings:
|
if not user_settings:
|
||||||
continue
|
continue
|
||||||
user_settings.initial_data["object_uid"] = stage.pk
|
user_settings.initial_data["object_uid"] = str(stage.pk)
|
||||||
if not user_settings.is_valid():
|
if not user_settings.is_valid():
|
||||||
LOGGER.warning(user_settings.errors)
|
LOGGER.warning(user_settings.errors)
|
||||||
matching_stages.append(user_settings.initial_data)
|
matching_stages.append(user_settings.initial_data)
|
||||||
|
|
|
@ -2,32 +2,12 @@
|
||||||
|
|
||||||
from django import template
|
from django import template
|
||||||
from django.db.models import Model
|
from django.db.models import Model
|
||||||
from django.template import Context
|
|
||||||
from structlog.stdlib import get_logger
|
from structlog.stdlib import get_logger
|
||||||
|
|
||||||
from authentik.lib.utils.urls import is_url_absolute
|
|
||||||
|
|
||||||
register = template.Library()
|
register = template.Library()
|
||||||
LOGGER = get_logger()
|
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")
|
@register.filter("fieldtype")
|
||||||
def fieldtype(field):
|
def fieldtype(field):
|
||||||
"""Return classname"""
|
"""Return classname"""
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
{% block card %}
|
{% block card %}
|
||||||
<form method="POST" class="pf-c-form">
|
<form method="POST" class="pf-c-form">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{% include 'partials/form.html' %}
|
|
||||||
<div class="pf-c-form__group">
|
<div class="pf-c-form__group">
|
||||||
<p>
|
<p>
|
||||||
<i class="pf-icon pf-icon-error-circle-o"></i>
|
<i class="pf-icon pf-icon-error-circle-o"></i>
|
||||||
|
@ -58,8 +57,5 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% if 'back' in request.GET %}
|
|
||||||
<a href="{% back %}" class="btn btn-primary btn-block btn-lg">{% trans 'Back' %}</a>
|
|
||||||
{% endif %}
|
|
||||||
</form>
|
</form>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -11,10 +11,10 @@ class UserOAuthSourceConnectionSerializer(SourceSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = UserOAuthSourceConnection
|
model = UserOAuthSourceConnection
|
||||||
fields = [
|
fields = [
|
||||||
|
"pk",
|
||||||
"user",
|
"user",
|
||||||
"source",
|
"source",
|
||||||
"identifier",
|
"identifier",
|
||||||
"access_token",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@ class UserOAuthSourceConnectionViewSet(ModelViewSet):
|
||||||
|
|
||||||
queryset = UserOAuthSourceConnection.objects.all()
|
queryset = UserOAuthSourceConnection.objects.all()
|
||||||
serializer_class = UserOAuthSourceConnectionSerializer
|
serializer_class = UserOAuthSourceConnectionSerializer
|
||||||
|
filterset_fields = ["source"]
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
if not self.request:
|
if not self.request:
|
||||||
|
|
|
@ -9,8 +9,7 @@ from django.utils.translation import gettext_lazy as _
|
||||||
from rest_framework.serializers import Serializer
|
from rest_framework.serializers import Serializer
|
||||||
|
|
||||||
from authentik.core.models import Source, UserSourceConnection
|
from authentik.core.models import Source, UserSourceConnection
|
||||||
from authentik.core.types import UILoginButton
|
from authentik.core.types import UILoginButton, UserSettingSerializer
|
||||||
from authentik.flows.challenge import Challenge, ChallengeTypes
|
|
||||||
|
|
||||||
|
|
||||||
class OAuthSource(Source):
|
class OAuthSource(Source):
|
||||||
|
@ -67,13 +66,11 @@ class OAuthSource(Source):
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def ui_user_settings(self) -> Optional[Challenge]:
|
def ui_user_settings(self) -> Optional[UserSettingSerializer]:
|
||||||
view_name = "authentik_sources_oauth:oauth-client-user"
|
return UserSettingSerializer(
|
||||||
return Challenge(
|
|
||||||
data={
|
data={
|
||||||
"type": ChallengeTypes.shell.value,
|
"title": f"OAuth {self.name}",
|
||||||
"title": self.name,
|
"component": "ak-user-settings-source-oauth",
|
||||||
"component": reverse(view_name, kwargs={"source_slug": self.slug}),
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
{% load i18n %}
|
|
||||||
|
|
||||||
<div class="pf-c-card">
|
|
||||||
<div class="pf-pf-c-card__title">
|
|
||||||
{% blocktrans with source_name=source.name %}
|
|
||||||
Source {{ source_name }}
|
|
||||||
{% endblocktrans %}
|
|
||||||
</div>
|
|
||||||
<div class="pf-c-card__body">
|
|
||||||
{% if connections.exists %}
|
|
||||||
<p>{% trans 'Connected.' %}</p>
|
|
||||||
<a class="pf-c-button pf-m-danger ak-root-link"
|
|
||||||
href="{% url 'authentik_sources_oauth:oauth-client-disconnect' source_slug=source.slug %}">
|
|
||||||
{% trans 'Disconnect' %}
|
|
||||||
</a>
|
|
||||||
{% else %}
|
|
||||||
<p>Not connected.</p>
|
|
||||||
<a class="pf-c-button pf-m-primary ak-root-link"
|
|
||||||
href="{% url 'authentik_sources_oauth:oauth-client-login' source_slug=source.slug %}">
|
|
||||||
{% trans 'Connect' %}
|
|
||||||
</a>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
|
@ -4,7 +4,6 @@ from django.urls import path
|
||||||
|
|
||||||
from authentik.sources.oauth.types.manager import RequestKind
|
from authentik.sources.oauth.types.manager import RequestKind
|
||||||
from authentik.sources.oauth.views.dispatcher import DispatcherView
|
from authentik.sources.oauth.views.dispatcher import DispatcherView
|
||||||
from authentik.sources.oauth.views.user import DisconnectView, UserSettingsView
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path(
|
path(
|
||||||
|
@ -17,14 +16,4 @@ urlpatterns = [
|
||||||
DispatcherView.as_view(kind=RequestKind.callback),
|
DispatcherView.as_view(kind=RequestKind.callback),
|
||||||
name="oauth-client-callback",
|
name="oauth-client-callback",
|
||||||
),
|
),
|
||||||
path(
|
|
||||||
"user/<slug:source_slug>/",
|
|
||||||
UserSettingsView.as_view(),
|
|
||||||
name="oauth-client-user",
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
"user/<slug:source_slug>/disconnect/",
|
|
||||||
DisconnectView.as_view(),
|
|
||||||
name="oauth-client-disconnect",
|
|
||||||
),
|
|
||||||
]
|
]
|
||||||
|
|
|
@ -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},
|
|
||||||
),
|
|
||||||
},
|
|
||||||
)
|
|
|
@ -9,6 +9,7 @@ from authentik.events.models import Event
|
||||||
@receiver(pre_delete, sender=StaticDevice)
|
@receiver(pre_delete, sender=StaticDevice)
|
||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
def pre_delete_event(sender, instance: StaticDevice, **_):
|
def pre_delete_event(sender, instance: StaticDevice, **_):
|
||||||
|
"""Create event before deleting Static Devices"""
|
||||||
# Create event with email notification
|
# Create event with email notification
|
||||||
event = Event.new(
|
event = Event.new(
|
||||||
"static_authenticator_disable", message="User disabled Static OTP Tokens."
|
"static_authenticator_disable", message="User disabled Static OTP Tokens."
|
||||||
|
|
|
@ -9,6 +9,7 @@ from authentik.events.models import Event
|
||||||
@receiver(pre_delete, sender=TOTPDevice)
|
@receiver(pre_delete, sender=TOTPDevice)
|
||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
def pre_delete_event(sender, instance: TOTPDevice, **_):
|
def pre_delete_event(sender, instance: TOTPDevice, **_):
|
||||||
|
"""Create event before deleting TOTP Devices"""
|
||||||
# Create event with email notification
|
# Create event with email notification
|
||||||
event = Event.new("totp_disable", message="User disabled Time-based OTP.")
|
event = Event.new("totp_disable", message="User disabled Time-based OTP.")
|
||||||
event.set_user(instance.user)
|
event.set_user(instance.user)
|
||||||
|
|
333
swagger.yaml
333
swagger.yaml
|
@ -1188,147 +1188,6 @@ paths:
|
||||||
required: true
|
required: true
|
||||||
type: string
|
type: string
|
||||||
format: uuid
|
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/:
|
/core/tokens/:
|
||||||
get:
|
get:
|
||||||
operationId: core_tokens_list
|
operationId: core_tokens_list
|
||||||
|
@ -7551,6 +7410,152 @@ paths:
|
||||||
type: string
|
type: string
|
||||||
format: slug
|
format: slug
|
||||||
pattern: ^[-a-zA-Z0-9_]+$
|
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/:
|
/sources/saml/:
|
||||||
get:
|
get:
|
||||||
operationId: sources_saml_list
|
operationId: sources_saml_list
|
||||||
|
@ -10963,29 +10968,6 @@ definitions:
|
||||||
attributes:
|
attributes:
|
||||||
title: Attributes
|
title: Attributes
|
||||||
type: object
|
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:
|
User:
|
||||||
title: User
|
title: User
|
||||||
description: User Serializer
|
description: User Serializer
|
||||||
|
@ -13742,6 +13724,29 @@ definitions:
|
||||||
title: Callback url
|
title: Callback url
|
||||||
type: string
|
type: string
|
||||||
readOnly: true
|
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:
|
SAMLSource:
|
||||||
description: SAMLSource Serializer
|
description: SAMLSource Serializer
|
||||||
required:
|
required:
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
"""Test Enroll flow"""
|
"""Test Enroll flow"""
|
||||||
from sys import platform
|
from sys import platform
|
||||||
|
from time import sleep
|
||||||
from typing import Any, Optional
|
from typing import Any, Optional
|
||||||
from unittest.case import skipUnless
|
from unittest.case import skipUnless
|
||||||
|
|
||||||
|
@ -190,6 +191,7 @@ class TestFlowsEnroll(SeleniumTestCase):
|
||||||
self.driver.close()
|
self.driver.close()
|
||||||
self.driver.switch_to.window(self.driver.window_handles[0])
|
self.driver.switch_to.window(self.driver.window_handles[0])
|
||||||
|
|
||||||
|
sleep(2)
|
||||||
# We're now logged in
|
# We're now logged in
|
||||||
wait = WebDriverWait(
|
wait = WebDriverWait(
|
||||||
self.get_shadow_root("ak-interface-admin"), self.wait_timeout
|
self.get_shadow_root("ak-interface-admin"), self.wait_timeout
|
||||||
|
|
|
@ -94,6 +94,9 @@ export class AppURLManager {
|
||||||
static sourceSAML(slug: string, rest: string): string {
|
static sourceSAML(slug: string, rest: string): string {
|
||||||
return `/source/saml/${slug}/${rest}`;
|
return `/source/saml/${slug}/${rest}`;
|
||||||
}
|
}
|
||||||
|
static sourceOAuth(slug: string, action: string): string {
|
||||||
|
return `/source/oauth/${action}/${slug}/`;
|
||||||
|
}
|
||||||
static providerSAML(rest: string): string {
|
static providerSAML(rest: string): string {
|
||||||
return `/application/saml/${rest}`;
|
return `/application/saml/${rest}`;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ import "./settings/UserSettingsAuthenticatorTOTP";
|
||||||
import "./settings/UserSettingsAuthenticatorStatic";
|
import "./settings/UserSettingsAuthenticatorStatic";
|
||||||
import "./settings/UserSettingsAuthenticatorWebAuthnDevices";
|
import "./settings/UserSettingsAuthenticatorWebAuthnDevices";
|
||||||
import "./settings/UserSettingsPassword";
|
import "./settings/UserSettingsPassword";
|
||||||
|
import "./settings/SourceSettingsOAuth";
|
||||||
|
|
||||||
@customElement("ak-user-settings")
|
@customElement("ak-user-settings")
|
||||||
export class UserSettingsPage extends LitElement {
|
export class UserSettingsPage extends LitElement {
|
||||||
|
@ -35,16 +36,16 @@ export class UserSettingsPage extends LitElement {
|
||||||
renderStageSettings(stage: UserSetting): TemplateResult {
|
renderStageSettings(stage: UserSetting): TemplateResult {
|
||||||
switch (stage.component) {
|
switch (stage.component) {
|
||||||
case "ak-user-settings-authenticator-webauthn":
|
case "ak-user-settings-authenticator-webauthn":
|
||||||
return html`<ak-user-settings-authenticator-webauthn stageId=${stage.objectUid}>
|
return html`<ak-user-settings-authenticator-webauthn objectId=${stage.objectUid}>
|
||||||
</ak-user-settings-authenticator-webauthn>`;
|
</ak-user-settings-authenticator-webauthn>`;
|
||||||
case "ak-user-settings-password":
|
case "ak-user-settings-password":
|
||||||
return html`<ak-user-settings-password stageId=${stage.objectUid}>
|
return html`<ak-user-settings-password objectId=${stage.objectUid}>
|
||||||
</ak-user-settings-password>`;
|
</ak-user-settings-password>`;
|
||||||
case "ak-user-settings-authenticator-totp":
|
case "ak-user-settings-authenticator-totp":
|
||||||
return html`<ak-user-settings-authenticator-totp stageId=${stage.objectUid}>
|
return html`<ak-user-settings-authenticator-totp objectId=${stage.objectUid}>
|
||||||
</ak-user-settings-authenticator-totp>`;
|
</ak-user-settings-authenticator-totp>`;
|
||||||
case "ak-user-settings-authenticator-static":
|
case "ak-user-settings-authenticator-static":
|
||||||
return html`<ak-user-settings-authenticator-static stageId=${stage.objectUid}>
|
return html`<ak-user-settings-authenticator-static objectId=${stage.objectUid}>
|
||||||
</ak-user-settings-authenticator-static>`;
|
</ak-user-settings-authenticator-static>`;
|
||||||
default:
|
default:
|
||||||
return html`<div class="pf-u-display-flex pf-u-justify-content-center">
|
return html`<div class="pf-u-display-flex pf-u-justify-content-center">
|
||||||
|
@ -57,6 +58,22 @@ export class UserSettingsPage extends LitElement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderSourceSettings(source: UserSetting): TemplateResult {
|
||||||
|
switch (source.component) {
|
||||||
|
case "ak-user-settings-source-oauth":
|
||||||
|
return html`<ak-user-settings-source-oauth objectId=${source.objectUid}>
|
||||||
|
</ak-user-settings-source-oauth>`;
|
||||||
|
default:
|
||||||
|
return html`<div class="pf-u-display-flex pf-u-justify-content-center">
|
||||||
|
<div class="pf-u-w-75">
|
||||||
|
<ak-site-shell url="${ifDefined(source.component)}">
|
||||||
|
<div slot="body"></div>
|
||||||
|
</ak-site-shell>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render(): TemplateResult {
|
render(): TemplateResult {
|
||||||
return html`<div class="pf-c-page">
|
return html`<div class="pf-c-page">
|
||||||
<main role="main" class="pf-c-page__main" tabindex="-1">
|
<main role="main" class="pf-c-page__main" tabindex="-1">
|
||||||
|
@ -89,17 +106,10 @@ export class UserSettingsPage extends LitElement {
|
||||||
</section>`;
|
</section>`;
|
||||||
});
|
});
|
||||||
}))}
|
}))}
|
||||||
${until(new SourcesApi(DEFAULT_CONFIG).sourcesAllUserSettings({}).then((sources) => {
|
${until(new SourcesApi(DEFAULT_CONFIG).sourcesAllUserSettings({}).then((source) => {
|
||||||
return sources.map((source) => {
|
return source.map((stage) => {
|
||||||
// TODO: Check for non-shell sources
|
return html`<section slot="page-${stage.objectUid}" data-tab-title="${ifDefined(stage.title)}" class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||||
return html`<section slot="page-${source.objectUid}" data-tab-title="${ifDefined(source.title)}" class="pf-c-page__main-section pf-m-no-padding-mobile">
|
${this.renderSourceSettings(stage)}
|
||||||
<div class="pf-u-display-flex pf-u-justify-content-center">
|
|
||||||
<div class="pf-u-w-75">
|
|
||||||
<ak-site-shell url="${ifDefined(source.component)}">
|
|
||||||
<div slot="body"></div>
|
|
||||||
</ak-site-shell>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>`;
|
</section>`;
|
||||||
});
|
});
|
||||||
}))}
|
}))}
|
||||||
|
|
|
@ -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`<div class="pf-c-card">
|
||||||
|
<div class="pf-pf-c-card__title">
|
||||||
|
${gettext(`Source ${source.name}`)}
|
||||||
|
</div>
|
||||||
|
<div class="pf-c-card__body">
|
||||||
|
${this.renderInner(source)}
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
}))}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderInner(source: OAuthSource): TemplateResult {
|
||||||
|
return html`${until(new SourcesApi(DEFAULT_CONFIG).sourcesOauthUserConnectionsList({
|
||||||
|
source: this.objectId
|
||||||
|
}).then((connection) => {
|
||||||
|
if (connection.results.length > 0) {
|
||||||
|
return html`<p>${gettext("Connected.")}</p>
|
||||||
|
<button class="pf-c-button pf-m-danger"
|
||||||
|
@click=${() => {
|
||||||
|
return new SourcesApi(DEFAULT_CONFIG).sourcesOauthUserConnectionsDelete({
|
||||||
|
id: connection.results[0].pk || 0
|
||||||
|
});
|
||||||
|
}}>
|
||||||
|
${gettext("Disconnect")}
|
||||||
|
</button>`;
|
||||||
|
}
|
||||||
|
return html`<p>${gettext("Not connected.")}</p>
|
||||||
|
<a class="pf-c-button pf-m-primary"
|
||||||
|
href=${AppURLManager.sourceOAuth(source.slug, "login")}>
|
||||||
|
${gettext("Connect")}
|
||||||
|
</a>`;
|
||||||
|
}))}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Reference in New Issue