tenants: add web certificate field, make authentik's core certificate configurable based on keypair
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
parent
9e2492be5c
commit
34b11524f1
|
@ -44,7 +44,7 @@ class CertificateBuilder:
|
||||||
"""Build self-signed certificate"""
|
"""Build self-signed certificate"""
|
||||||
one_day = datetime.timedelta(1, 0, 0)
|
one_day = datetime.timedelta(1, 0, 0)
|
||||||
self.__private_key = rsa.generate_private_key(
|
self.__private_key = rsa.generate_private_key(
|
||||||
public_exponent=65537, key_size=2048, backend=default_backend()
|
public_exponent=65537, key_size=4096, backend=default_backend()
|
||||||
)
|
)
|
||||||
self.__public_key = self.__private_key.public_key()
|
self.__public_key = self.__private_key.public_key()
|
||||||
alt_names: list[x509.GeneralName] = [x509.DNSName(x) for x in subject_alt_names or []]
|
alt_names: list[x509.GeneralName] = [x509.DNSName(x) for x in subject_alt_names or []]
|
||||||
|
|
|
@ -45,6 +45,7 @@ from authentik.lib.utils.errors import exception_to_string
|
||||||
from authentik.managed.models import ManagedModel
|
from authentik.managed.models import ManagedModel
|
||||||
from authentik.outposts.controllers.k8s.utils import get_namespace
|
from authentik.outposts.controllers.k8s.utils import get_namespace
|
||||||
from authentik.outposts.docker_tls import DockerInlineTLS
|
from authentik.outposts.docker_tls import DockerInlineTLS
|
||||||
|
from authentik.tenants.models import Tenant
|
||||||
|
|
||||||
OUR_VERSION = parse(__version__)
|
OUR_VERSION = parse(__version__)
|
||||||
OUTPOST_HELLO_INTERVAL = 10
|
OUTPOST_HELLO_INTERVAL = 10
|
||||||
|
@ -385,7 +386,8 @@ class Outpost(ManagedModel):
|
||||||
user.user_permissions.add(permission.first())
|
user.user_permissions.add(permission.first())
|
||||||
LOGGER.debug(
|
LOGGER.debug(
|
||||||
"Updated service account's permissions",
|
"Updated service account's permissions",
|
||||||
perms=UserObjectPermission.objects.filter(user=user),
|
obj_perms=UserObjectPermission.objects.filter(user=user),
|
||||||
|
perms=user.user_permissions.all(),
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -449,6 +451,10 @@ class Outpost(ManagedModel):
|
||||||
objects.extend(provider.get_required_objects())
|
objects.extend(provider.get_required_objects())
|
||||||
else:
|
else:
|
||||||
objects.append(provider)
|
objects.append(provider)
|
||||||
|
if self.managed:
|
||||||
|
for tenant in Tenant.objects.filter(web_certificate__isnull=False):
|
||||||
|
objects.append(tenant)
|
||||||
|
objects.append(tenant.web_certificate)
|
||||||
return objects
|
return objects
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
|
|
|
@ -10,6 +10,7 @@ from authentik.crypto.models import CertificateKeyPair
|
||||||
from authentik.lib.utils.reflection import class_to_path
|
from authentik.lib.utils.reflection import class_to_path
|
||||||
from authentik.outposts.models import Outpost, OutpostServiceConnection
|
from authentik.outposts.models import Outpost, OutpostServiceConnection
|
||||||
from authentik.outposts.tasks import CACHE_KEY_OUTPOST_DOWN, outpost_controller, outpost_post_save
|
from authentik.outposts.tasks import CACHE_KEY_OUTPOST_DOWN, outpost_controller, outpost_post_save
|
||||||
|
from authentik.tenants.models import Tenant
|
||||||
|
|
||||||
LOGGER = get_logger()
|
LOGGER = get_logger()
|
||||||
UPDATE_TRIGGERING_MODELS = (
|
UPDATE_TRIGGERING_MODELS = (
|
||||||
|
@ -17,6 +18,7 @@ UPDATE_TRIGGERING_MODELS = (
|
||||||
OutpostServiceConnection,
|
OutpostServiceConnection,
|
||||||
Provider,
|
Provider,
|
||||||
CertificateKeyPair,
|
CertificateKeyPair,
|
||||||
|
Tenant,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,7 @@ class TenantSerializer(ModelSerializer):
|
||||||
"flow_recovery",
|
"flow_recovery",
|
||||||
"flow_unenrollment",
|
"flow_unenrollment",
|
||||||
"event_retention",
|
"event_retention",
|
||||||
|
"web_certificate",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -69,6 +70,7 @@ class TenantViewSet(UsedByMixin, ModelViewSet):
|
||||||
search_fields = [
|
search_fields = [
|
||||||
"domain",
|
"domain",
|
||||||
"branding_title",
|
"branding_title",
|
||||||
|
"web_certificate__name",
|
||||||
]
|
]
|
||||||
filterset_fields = "__all__"
|
filterset_fields = "__all__"
|
||||||
ordering = ["domain"]
|
ordering = ["domain"]
|
||||||
|
|
|
@ -0,0 +1,146 @@
|
||||||
|
# Generated by Django 4.0 on 2021-12-22 09:42
|
||||||
|
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.apps.registry import Apps
|
||||||
|
from django.db import migrations, models
|
||||||
|
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
|
||||||
|
|
||||||
|
import authentik.lib.utils.time
|
||||||
|
|
||||||
|
|
||||||
|
def create_default_tenant(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
|
||||||
|
Flow = apps.get_model("authentik_flows", "Flow")
|
||||||
|
Tenant = apps.get_model("authentik_tenants", "Tenant")
|
||||||
|
|
||||||
|
db_alias = schema_editor.connection.alias
|
||||||
|
|
||||||
|
default_authentication = (
|
||||||
|
Flow.objects.using(db_alias).filter(slug="default-authentication-flow").first()
|
||||||
|
)
|
||||||
|
default_invalidation = (
|
||||||
|
Flow.objects.using(db_alias).filter(slug="default-invalidation-flow").first()
|
||||||
|
)
|
||||||
|
|
||||||
|
tenant, _ = Tenant.objects.using(db_alias).update_or_create(
|
||||||
|
domain="authentik-default",
|
||||||
|
default=True,
|
||||||
|
defaults={
|
||||||
|
"flow_authentication": default_authentication,
|
||||||
|
"flow_invalidation": default_invalidation,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
replaces = [
|
||||||
|
("authentik_tenants", "0001_initial"),
|
||||||
|
("authentik_tenants", "0002_default"),
|
||||||
|
("authentik_tenants", "0003_tenant_branding_favicon"),
|
||||||
|
("authentik_tenants", "0004_tenant_event_retention"),
|
||||||
|
("authentik_tenants", "0005_tenant_web_certificate"),
|
||||||
|
]
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("authentik_flows", "0018_oob_flows"),
|
||||||
|
("authentik_flows", "0008_default_flows"),
|
||||||
|
("authentik_crypto", "0003_certificatekeypair_managed"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="Tenant",
|
||||||
|
fields=[
|
||||||
|
(
|
||||||
|
"tenant_uuid",
|
||||||
|
models.UUIDField(
|
||||||
|
default=uuid.uuid4, editable=False, primary_key=True, serialize=False
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"domain",
|
||||||
|
models.TextField(
|
||||||
|
help_text="Domain that activates this tenant. Can be a superset, i.e. `a.b` for `aa.b` and `ba.b`"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("default", models.BooleanField(default=False)),
|
||||||
|
("branding_title", models.TextField(default="authentik")),
|
||||||
|
(
|
||||||
|
"branding_logo",
|
||||||
|
models.TextField(default="/static/dist/assets/icons/icon_left_brand.svg"),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"flow_authentication",
|
||||||
|
models.ForeignKey(
|
||||||
|
null=True,
|
||||||
|
on_delete=django.db.models.deletion.SET_NULL,
|
||||||
|
related_name="tenant_authentication",
|
||||||
|
to="authentik_flows.flow",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"flow_invalidation",
|
||||||
|
models.ForeignKey(
|
||||||
|
null=True,
|
||||||
|
on_delete=django.db.models.deletion.SET_NULL,
|
||||||
|
related_name="tenant_invalidation",
|
||||||
|
to="authentik_flows.flow",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"flow_recovery",
|
||||||
|
models.ForeignKey(
|
||||||
|
null=True,
|
||||||
|
on_delete=django.db.models.deletion.SET_NULL,
|
||||||
|
related_name="tenant_recovery",
|
||||||
|
to="authentik_flows.flow",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"flow_unenrollment",
|
||||||
|
models.ForeignKey(
|
||||||
|
null=True,
|
||||||
|
on_delete=django.db.models.deletion.SET_NULL,
|
||||||
|
related_name="tenant_unenrollment",
|
||||||
|
to="authentik_flows.flow",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
"verbose_name": "Tenant",
|
||||||
|
"verbose_name_plural": "Tenants",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.RunPython(
|
||||||
|
code=create_default_tenant,
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="tenant",
|
||||||
|
name="branding_favicon",
|
||||||
|
field=models.TextField(default="/static/dist/assets/icons/icon.png"),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="tenant",
|
||||||
|
name="event_retention",
|
||||||
|
field=models.TextField(
|
||||||
|
default="days=365",
|
||||||
|
help_text="Events will be deleted after this duration.(Format: weeks=3;days=2;hours=3,seconds=2).",
|
||||||
|
validators=[authentik.lib.utils.time.timedelta_string_validator],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="tenant",
|
||||||
|
name="web_certificate",
|
||||||
|
field=models.ForeignKey(
|
||||||
|
default=None,
|
||||||
|
help_text="Web Certificate used by the authentik Core webserver.",
|
||||||
|
null=True,
|
||||||
|
on_delete=django.db.models.deletion.SET_DEFAULT,
|
||||||
|
to="authentik_crypto.certificatekeypair",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,26 @@
|
||||||
|
# Generated by Django 4.0 on 2021-12-22 09:42
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("authentik_crypto", "0003_certificatekeypair_managed"),
|
||||||
|
("authentik_tenants", "0004_tenant_event_retention"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="tenant",
|
||||||
|
name="web_certificate",
|
||||||
|
field=models.ForeignKey(
|
||||||
|
default=None,
|
||||||
|
help_text="Web Certificate used by the authentik Core webserver.",
|
||||||
|
null=True,
|
||||||
|
on_delete=django.db.models.deletion.SET_DEFAULT,
|
||||||
|
to="authentik_crypto.certificatekeypair",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
|
@ -4,6 +4,7 @@ from uuid import uuid4
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
from authentik.crypto.models import CertificateKeyPair
|
||||||
from authentik.flows.models import Flow
|
from authentik.flows.models import Flow
|
||||||
from authentik.lib.utils.time import timedelta_string_validator
|
from authentik.lib.utils.time import timedelta_string_validator
|
||||||
|
|
||||||
|
@ -51,6 +52,14 @@ class Tenant(models.Model):
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
web_certificate = models.ForeignKey(
|
||||||
|
CertificateKeyPair,
|
||||||
|
null=True,
|
||||||
|
default=None,
|
||||||
|
on_delete=models.SET_DEFAULT,
|
||||||
|
help_text=_(("Web Certificate used by the authentik Core webserver.")),
|
||||||
|
)
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
if self.default:
|
if self.default:
|
||||||
return "Default tenant"
|
return "Default tenant"
|
||||||
|
|
|
@ -15,6 +15,7 @@ import (
|
||||||
"goauthentik.io/internal/outpost/ak"
|
"goauthentik.io/internal/outpost/ak"
|
||||||
"goauthentik.io/internal/outpost/proxyv2"
|
"goauthentik.io/internal/outpost/proxyv2"
|
||||||
"goauthentik.io/internal/web"
|
"goauthentik.io/internal/web"
|
||||||
|
"goauthentik.io/internal/web/tenant_tls"
|
||||||
)
|
)
|
||||||
|
|
||||||
var running = true
|
var running = true
|
||||||
|
@ -110,6 +111,12 @@ func attemptProxyStart(ws *web.WebServer, u *url.URL) {
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
// Init tenant_tls here too since it requires an API Client,
|
||||||
|
// so we just re-use the same one as the outpost uses
|
||||||
|
tw := tenant_tls.NewWatcher(ac.Client)
|
||||||
|
go tw.Start()
|
||||||
|
ws.TenantTLS = tw
|
||||||
|
|
||||||
srv := proxyv2.NewProxyServer(ac, 0)
|
srv := proxyv2.NewProxyServer(ac, 0)
|
||||||
ws.ProxyServer = srv
|
ws.ProxyServer = srv
|
||||||
ac.Server = srv
|
ac.Server = srv
|
||||||
|
|
|
@ -6,7 +6,6 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/getsentry/sentry-go"
|
"github.com/getsentry/sentry-go"
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type tracingTransport struct {
|
type tracingTransport struct {
|
||||||
|
@ -26,9 +25,5 @@ func (tt *tracingTransport) RoundTrip(r *http.Request) (*http.Response, error) {
|
||||||
span.SetTag("method", r.Method)
|
span.SetTag("method", r.Method)
|
||||||
defer span.Finish()
|
defer span.Finish()
|
||||||
res, err := tt.inner.RoundTrip(r.WithContext(span.Context()))
|
res, err := tt.inner.RoundTrip(r.WithContext(span.Context()))
|
||||||
log.WithFields(log.Fields{
|
|
||||||
"url": r.URL.String(),
|
|
||||||
"method": r.Method,
|
|
||||||
}).Trace("http request")
|
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
package tenant_tls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"goauthentik.io/api"
|
||||||
|
"goauthentik.io/internal/crypto"
|
||||||
|
"goauthentik.io/internal/outpost/ak"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Watcher struct {
|
||||||
|
client *api.APIClient
|
||||||
|
log *log.Entry
|
||||||
|
cs *ak.CryptoStore
|
||||||
|
fallback *tls.Certificate
|
||||||
|
tenants []api.Tenant
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWatcher(client *api.APIClient) *Watcher {
|
||||||
|
cs := ak.NewCryptoStore(client.CryptoApi)
|
||||||
|
l := log.WithField("logger", "authentik.router.tenant_tls")
|
||||||
|
cert, err := crypto.GenerateSelfSignedCert()
|
||||||
|
if err != nil {
|
||||||
|
l.WithError(err).Error("failed to generate default cert")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Watcher{
|
||||||
|
client: client,
|
||||||
|
log: l,
|
||||||
|
cs: cs,
|
||||||
|
fallback: &cert,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Watcher) Start() {
|
||||||
|
ticker := time.NewTicker(time.Minute * 3)
|
||||||
|
w.log.Info("Starting Tenant TLS Checker")
|
||||||
|
for ; true; <-ticker.C {
|
||||||
|
w.Check()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Watcher) Check() {
|
||||||
|
tenants, _, err := w.client.CoreApi.CoreTenantsListExecute(api.ApiCoreTenantsListRequest{})
|
||||||
|
if err != nil {
|
||||||
|
w.log.WithError(err).Warning("failed to get tenants")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, t := range tenants.Results {
|
||||||
|
if t.WebCertificate.IsSet() {
|
||||||
|
err := w.cs.AddKeypair(*t.WebCertificate.Get())
|
||||||
|
if err != nil {
|
||||||
|
w.log.WithError(err).Warning("failed to add certificate")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w.tenants = tenants.Results
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Watcher) GetCertificate(ch *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||||
|
var bestSelection *api.Tenant
|
||||||
|
for _, t := range w.tenants {
|
||||||
|
if !t.WebCertificate.IsSet() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if *t.Default {
|
||||||
|
bestSelection = &t
|
||||||
|
}
|
||||||
|
if strings.HasSuffix(ch.ServerName, t.Domain) {
|
||||||
|
bestSelection = &t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if bestSelection == nil {
|
||||||
|
return w.fallback, nil
|
||||||
|
}
|
||||||
|
cert := w.cs.Get(*bestSelection.WebCertificate.Get())
|
||||||
|
return cert, nil
|
||||||
|
}
|
|
@ -22,6 +22,9 @@ func (ws *WebServer) GetCertificate() func(ch *tls.ClientHelloInfo) (*tls.Certif
|
||||||
return appCert, nil
|
return appCert, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if ws.TenantTLS != nil {
|
||||||
|
return ws.TenantTLS.GetCertificate(ch)
|
||||||
|
}
|
||||||
ws.log.Trace("using default, self-signed certificate")
|
ws.log.Trace("using default, self-signed certificate")
|
||||||
return &cert, nil
|
return &cert, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,17 +15,17 @@ import (
|
||||||
"goauthentik.io/internal/gounicorn"
|
"goauthentik.io/internal/gounicorn"
|
||||||
"goauthentik.io/internal/outpost/proxyv2"
|
"goauthentik.io/internal/outpost/proxyv2"
|
||||||
"goauthentik.io/internal/utils/web"
|
"goauthentik.io/internal/utils/web"
|
||||||
|
"goauthentik.io/internal/web/tenant_tls"
|
||||||
)
|
)
|
||||||
|
|
||||||
type WebServer struct {
|
type WebServer struct {
|
||||||
Bind string
|
Bind string
|
||||||
BindTLS bool
|
BindTLS bool
|
||||||
|
|
||||||
LegacyProxy bool
|
|
||||||
|
|
||||||
stop chan struct{} // channel for waiting shutdown
|
stop chan struct{} // channel for waiting shutdown
|
||||||
|
|
||||||
ProxyServer *proxyv2.ProxyServer
|
ProxyServer *proxyv2.ProxyServer
|
||||||
|
TenantTLS *tenant_tls.Watcher
|
||||||
|
|
||||||
m *mux.Router
|
m *mux.Router
|
||||||
lh *mux.Router
|
lh *mux.Router
|
||||||
|
@ -43,8 +43,6 @@ func NewWebServer(g *gounicorn.GoUnicorn) *WebServer {
|
||||||
logginRouter.Use(web.NewLoggingHandler(l, nil))
|
logginRouter.Use(web.NewLoggingHandler(l, nil))
|
||||||
|
|
||||||
ws := &WebServer{
|
ws := &WebServer{
|
||||||
LegacyProxy: true,
|
|
||||||
|
|
||||||
m: mainHandler,
|
m: mainHandler,
|
||||||
lh: logginRouter,
|
lh: logginRouter,
|
||||||
log: l,
|
log: l,
|
||||||
|
|
20
schema.yml
20
schema.yml
|
@ -2371,6 +2371,11 @@ paths:
|
||||||
schema:
|
schema:
|
||||||
type: string
|
type: string
|
||||||
format: uuid
|
format: uuid
|
||||||
|
- in: query
|
||||||
|
name: web_certificate
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
tags:
|
tags:
|
||||||
- core
|
- core
|
||||||
security:
|
security:
|
||||||
|
@ -28211,6 +28216,11 @@ components:
|
||||||
type: string
|
type: string
|
||||||
minLength: 1
|
minLength: 1
|
||||||
description: 'Events will be deleted after this duration.(Format: weeks=3;days=2;hours=3,seconds=2).'
|
description: 'Events will be deleted after this duration.(Format: weeks=3;days=2;hours=3,seconds=2).'
|
||||||
|
web_certificate:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
nullable: true
|
||||||
|
description: Web Certificate used by the authentik Core webserver.
|
||||||
PatchedTokenRequest:
|
PatchedTokenRequest:
|
||||||
type: object
|
type: object
|
||||||
description: Token Serializer
|
description: Token Serializer
|
||||||
|
@ -30575,6 +30585,11 @@ components:
|
||||||
event_retention:
|
event_retention:
|
||||||
type: string
|
type: string
|
||||||
description: 'Events will be deleted after this duration.(Format: weeks=3;days=2;hours=3,seconds=2).'
|
description: 'Events will be deleted after this duration.(Format: weeks=3;days=2;hours=3,seconds=2).'
|
||||||
|
web_certificate:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
nullable: true
|
||||||
|
description: Web Certificate used by the authentik Core webserver.
|
||||||
required:
|
required:
|
||||||
- domain
|
- domain
|
||||||
- tenant_uuid
|
- tenant_uuid
|
||||||
|
@ -30618,6 +30633,11 @@ components:
|
||||||
type: string
|
type: string
|
||||||
minLength: 1
|
minLength: 1
|
||||||
description: 'Events will be deleted after this duration.(Format: weeks=3;days=2;hours=3,seconds=2).'
|
description: 'Events will be deleted after this duration.(Format: weeks=3;days=2;hours=3,seconds=2).'
|
||||||
|
web_certificate:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
nullable: true
|
||||||
|
description: Web Certificate used by the authentik Core webserver.
|
||||||
required:
|
required:
|
||||||
- domain
|
- domain
|
||||||
Token:
|
Token:
|
||||||
|
|
|
@ -2709,6 +2709,7 @@ msgstr "Loading"
|
||||||
#: src/pages/tenants/TenantForm.ts
|
#: src/pages/tenants/TenantForm.ts
|
||||||
#: src/pages/tenants/TenantForm.ts
|
#: src/pages/tenants/TenantForm.ts
|
||||||
#: src/pages/tenants/TenantForm.ts
|
#: src/pages/tenants/TenantForm.ts
|
||||||
|
#: src/pages/tenants/TenantForm.ts
|
||||||
#: src/pages/tokens/TokenForm.ts
|
#: src/pages/tokens/TokenForm.ts
|
||||||
#: src/pages/users/UserForm.ts
|
#: src/pages/users/UserForm.ts
|
||||||
#: src/pages/users/UserResetEmailForm.ts
|
#: src/pages/users/UserResetEmailForm.ts
|
||||||
|
@ -5809,6 +5810,10 @@ msgstr "Warning: You're about to delete the user you're logged in as ({0}). Proc
|
||||||
msgid "Warning: authentik Domain is not configured, authentication will not work."
|
msgid "Warning: authentik Domain is not configured, authentication will not work."
|
||||||
msgstr "Warning: authentik Domain is not configured, authentication will not work."
|
msgstr "Warning: authentik Domain is not configured, authentication will not work."
|
||||||
|
|
||||||
|
#: src/pages/tenants/TenantForm.ts
|
||||||
|
msgid "Web Certificate"
|
||||||
|
msgstr "Web Certificate"
|
||||||
|
|
||||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||||
msgid "WebAuthn Authenticators"
|
msgid "WebAuthn Authenticators"
|
||||||
msgstr "WebAuthn Authenticators"
|
msgstr "WebAuthn Authenticators"
|
||||||
|
|
|
@ -2688,6 +2688,7 @@ msgstr "Chargement en cours"
|
||||||
#: src/pages/tenants/TenantForm.ts
|
#: src/pages/tenants/TenantForm.ts
|
||||||
#: src/pages/tenants/TenantForm.ts
|
#: src/pages/tenants/TenantForm.ts
|
||||||
#: src/pages/tenants/TenantForm.ts
|
#: src/pages/tenants/TenantForm.ts
|
||||||
|
#: src/pages/tenants/TenantForm.ts
|
||||||
#: src/pages/tokens/TokenForm.ts
|
#: src/pages/tokens/TokenForm.ts
|
||||||
#: src/pages/users/UserForm.ts
|
#: src/pages/users/UserForm.ts
|
||||||
#: src/pages/users/UserResetEmailForm.ts
|
#: src/pages/users/UserResetEmailForm.ts
|
||||||
|
@ -5747,6 +5748,10 @@ msgstr ""
|
||||||
msgid "Warning: authentik Domain is not configured, authentication will not work."
|
msgid "Warning: authentik Domain is not configured, authentication will not work."
|
||||||
msgstr "Avertissement : le domaine d'authentik n'est pas configuré, l'authentification ne fonctionnera pas."
|
msgstr "Avertissement : le domaine d'authentik n'est pas configuré, l'authentification ne fonctionnera pas."
|
||||||
|
|
||||||
|
#: src/pages/tenants/TenantForm.ts
|
||||||
|
msgid "Web Certificate"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||||
msgid "WebAuthn Authenticators"
|
msgid "WebAuthn Authenticators"
|
||||||
msgstr "Authentificateurs WebAuthn"
|
msgstr "Authentificateurs WebAuthn"
|
||||||
|
|
|
@ -2699,6 +2699,7 @@ msgstr ""
|
||||||
#: src/pages/tenants/TenantForm.ts
|
#: src/pages/tenants/TenantForm.ts
|
||||||
#: src/pages/tenants/TenantForm.ts
|
#: src/pages/tenants/TenantForm.ts
|
||||||
#: src/pages/tenants/TenantForm.ts
|
#: src/pages/tenants/TenantForm.ts
|
||||||
|
#: src/pages/tenants/TenantForm.ts
|
||||||
#: src/pages/tokens/TokenForm.ts
|
#: src/pages/tokens/TokenForm.ts
|
||||||
#: src/pages/users/UserForm.ts
|
#: src/pages/users/UserForm.ts
|
||||||
#: src/pages/users/UserResetEmailForm.ts
|
#: src/pages/users/UserResetEmailForm.ts
|
||||||
|
@ -5789,6 +5790,10 @@ msgstr ""
|
||||||
msgid "Warning: authentik Domain is not configured, authentication will not work."
|
msgid "Warning: authentik Domain is not configured, authentication will not work."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/pages/tenants/TenantForm.ts
|
||||||
|
msgid "Web Certificate"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||||
msgid "WebAuthn Authenticators"
|
msgid "WebAuthn Authenticators"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
|
@ -2,9 +2,16 @@ import { t } from "@lingui/macro";
|
||||||
|
|
||||||
import { TemplateResult, html } from "lit";
|
import { TemplateResult, html } from "lit";
|
||||||
import { customElement } from "lit/decorators.js";
|
import { customElement } from "lit/decorators.js";
|
||||||
|
import { ifDefined } from "lit/directives/if-defined.js";
|
||||||
import { until } from "lit/directives/until.js";
|
import { until } from "lit/directives/until.js";
|
||||||
|
|
||||||
import { CoreApi, FlowsApi, FlowsInstancesListDesignationEnum, Tenant } from "@goauthentik/api";
|
import {
|
||||||
|
CoreApi,
|
||||||
|
CryptoApi,
|
||||||
|
FlowsApi,
|
||||||
|
FlowsInstancesListDesignationEnum,
|
||||||
|
Tenant,
|
||||||
|
} from "@goauthentik/api";
|
||||||
|
|
||||||
import { DEFAULT_CONFIG } from "../../api/Config";
|
import { DEFAULT_CONFIG } from "../../api/Config";
|
||||||
import "../../elements/forms/FormGroup";
|
import "../../elements/forms/FormGroup";
|
||||||
|
@ -297,6 +304,35 @@ export class TenantForm extends ModelForm<Tenant, string> {
|
||||||
${t`Format: "weeks=3;days=2;hours=3,seconds=2".`}
|
${t`Format: "weeks=3;days=2;hours=3,seconds=2".`}
|
||||||
</p>
|
</p>
|
||||||
</ak-form-element-horizontal>
|
</ak-form-element-horizontal>
|
||||||
|
<ak-form-element-horizontal label=${t`Web Certificate`} name="webCertificate">
|
||||||
|
<select class="pf-c-form-control">
|
||||||
|
<option
|
||||||
|
value=""
|
||||||
|
?selected=${this.instance?.webCertificate === undefined}
|
||||||
|
>
|
||||||
|
---------
|
||||||
|
</option>
|
||||||
|
${until(
|
||||||
|
new CryptoApi(DEFAULT_CONFIG)
|
||||||
|
.cryptoCertificatekeypairsList({
|
||||||
|
ordering: "name",
|
||||||
|
hasKey: true,
|
||||||
|
})
|
||||||
|
.then((keys) => {
|
||||||
|
return keys.results.map((key) => {
|
||||||
|
return html`<option
|
||||||
|
value=${ifDefined(key.pk)}
|
||||||
|
?selected=${this.instance?.webCertificate ===
|
||||||
|
key.pk}
|
||||||
|
>
|
||||||
|
${key.name}
|
||||||
|
</option>`;
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
html`<option>${t`Loading...`}</option>`,
|
||||||
|
)}
|
||||||
|
</select>
|
||||||
|
</ak-form-element-horizontal>
|
||||||
</div>
|
</div>
|
||||||
</ak-form-group>
|
</ak-form-group>
|
||||||
</form>`;
|
</form>`;
|
||||||
|
|
Reference in New Issue