This repository has been archived on 2024-05-31. You can view files and clone it, but cannot push or open issues or pull requests.
authentik/passbook/providers/proxy/models.py

155 lines
4.5 KiB
Python
Raw Normal View History

2020-08-19 08:32:44 +00:00
"""passbook proxy models"""
2020-09-02 22:04:12 +00:00
import string
from random import SystemRandom
from typing import Iterable, Optional, Type
2020-09-02 22:04:12 +00:00
from urllib.parse import urljoin
2020-08-19 08:32:44 +00:00
from django.db import models
from django.forms import ModelForm
from django.http import HttpRequest
2020-08-19 08:32:44 +00:00
from django.utils.translation import gettext as _
2020-09-02 22:04:12 +00:00
from passbook.crypto.models import CertificateKeyPair
from passbook.lib.models import DomainlessURLValidator
2020-09-02 22:04:12 +00:00
from passbook.outposts.models import OutpostModel
2020-08-19 08:32:44 +00:00
from passbook.providers.oauth2.constants import (
SCOPE_OPENID,
SCOPE_OPENID_EMAIL,
SCOPE_OPENID_PROFILE,
)
from passbook.providers.oauth2.models import (
ClientTypes,
JWTAlgorithms,
OAuth2Provider,
ResponseTypes,
ScopeMapping,
)
SCOPE_PB_PROXY = "pb_proxy"
2020-08-19 08:32:44 +00:00
2020-09-02 22:04:12 +00:00
def get_cookie_secret():
"""Generate random 32-character string for cookie-secret"""
return "".join(
SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(32)
)
def _get_callback_url(uri: str) -> str:
return urljoin(uri, "/pbprox/callback")
class ProxyProvider(OutpostModel, OAuth2Provider):
2020-08-19 08:32:44 +00:00
"""Protect applications that don't support any of the other
Protocols by using a Reverse-Proxy."""
internal_host = models.TextField(
validators=[DomainlessURLValidator(schemes=("http", "https"))]
2020-08-19 08:32:44 +00:00
)
external_host = models.TextField(
validators=[DomainlessURLValidator(schemes=("http", "https"))]
2020-08-19 08:32:44 +00:00
)
internal_host_ssl_validation = models.BooleanField(
default=True,
help_text=_("Validate SSL Certificates of upstream servers"),
verbose_name=_("Internal host SSL Validation"),
2020-09-23 10:33:33 +00:00
)
2020-09-02 22:04:12 +00:00
skip_path_regex = models.TextField(
default="",
blank=True,
2020-09-19 10:21:31 +00:00
help_text=_(
(
"Regular expressions for which authentication is not required. "
2020-09-19 10:21:31 +00:00
"Each new line is interpreted as a new Regular Expression."
)
),
)
basic_auth_enabled = models.BooleanField(
default=False,
verbose_name=_("Set HTTP-Basic Authentication"),
help_text=_(
"Set a custom HTTP-Basic Authentication header based on values from passbook."
),
)
basic_auth_user_attribute = models.TextField(
blank=True,
verbose_name=_("HTTP-Basic Username Key"),
help_text=_(
(
"User/Group Attribute used for the user part of the HTTP-Basic Header. "
"If not set, the user's Email address is used."
)
),
)
basic_auth_password_attribute = models.TextField(
blank=True,
verbose_name=_("HTTP-Basic Password Key"),
help_text=_(
(
"User/Group Attribute used for the password part of the HTTP-Basic Header."
)
),
)
2020-09-02 22:04:12 +00:00
certificate = models.ForeignKey(
2020-09-30 17:34:22 +00:00
CertificateKeyPair,
on_delete=models.SET_NULL,
null=True,
blank=True,
2020-09-02 22:04:12 +00:00
)
cookie_secret = models.TextField(default=get_cookie_secret)
@property
2020-08-19 08:32:44 +00:00
def form(self) -> Type[ModelForm]:
from passbook.providers.proxy.forms import ProxyProviderForm
return ProxyProviderForm
@property
def launch_url(self) -> Optional[str]:
"""Use external_host as launch URL"""
return self.external_host
def html_setup_urls(self, request: HttpRequest) -> Optional[str]:
"""Overwrite Setup URLs as they are not needed for proxy"""
return None
2020-08-19 08:32:44 +00:00
def set_oauth_defaults(self):
"""Ensure all OAuth2-related settings are correct"""
self.client_type = ClientTypes.CONFIDENTIAL
self.response_type = ResponseTypes.CODE
2020-09-02 22:04:12 +00:00
self.jwt_alg = JWTAlgorithms.RS256
self.rsa_key = CertificateKeyPair.objects.first()
2020-08-19 08:32:44 +00:00
scopes = ScopeMapping.objects.filter(
scope_name__in=[
SCOPE_OPENID,
SCOPE_OPENID_PROFILE,
SCOPE_OPENID_EMAIL,
SCOPE_PB_PROXY,
]
2020-08-19 08:32:44 +00:00
)
self.property_mappings.set(scopes)
self.redirect_uris = "\n".join(
[
2020-09-02 22:04:12 +00:00
_get_callback_url(self.external_host),
_get_callback_url(self.internal_host),
2020-08-19 08:32:44 +00:00
]
)
def __str__(self):
2020-09-02 22:04:12 +00:00
return f"Proxy Provider {self.name}"
def get_required_objects(self) -> Iterable[models.Model]:
required_models = [self]
if self.certificate is not None:
required_models.append(self.certificate)
return required_models
2020-08-19 08:32:44 +00:00
class Meta:
verbose_name = _("Proxy Provider")
verbose_name_plural = _("Proxy Providers")