providers/ldap: add LDAP provider

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
Jens Langhammer 2021-04-26 11:52:42 +02:00
parent 2f3bf5efe7
commit f89479caf3
12 changed files with 653 additions and 2 deletions

View File

@ -47,6 +47,7 @@ from authentik.policies.reputation.api import (
ReputationPolicyViewSet, ReputationPolicyViewSet,
UserReputationViewSet, UserReputationViewSet,
) )
from authentik.providers.ldap.api import LDAPOutpostConfigViewSet, LDAPProviderViewSet
from authentik.providers.oauth2.api.provider import OAuth2ProviderViewSet from authentik.providers.oauth2.api.provider import OAuth2ProviderViewSet
from authentik.providers.oauth2.api.scope import ScopeMappingViewSet from authentik.providers.oauth2.api.scope import ScopeMappingViewSet
from authentik.providers.oauth2.api.tokens import ( from authentik.providers.oauth2.api.tokens import (
@ -120,6 +121,7 @@ router.register(
"outposts/service_connections/kubernetes", KubernetesServiceConnectionViewSet "outposts/service_connections/kubernetes", KubernetesServiceConnectionViewSet
) )
router.register("outposts/proxy", ProxyOutpostConfigViewSet) router.register("outposts/proxy", ProxyOutpostConfigViewSet)
router.register("outposts/ldap", LDAPOutpostConfigViewSet)
router.register("flows/instances", FlowViewSet) router.register("flows/instances", FlowViewSet)
router.register("flows/bindings", FlowStageBindingViewSet) router.register("flows/bindings", FlowStageBindingViewSet)
@ -149,6 +151,7 @@ router.register("policies/reputation/ips", IPReputationViewSet)
router.register("policies/reputation", ReputationPolicyViewSet) router.register("policies/reputation", ReputationPolicyViewSet)
router.register("providers/all", ProviderViewSet) router.register("providers/all", ProviderViewSet)
router.register("providers/ldap", LDAPProviderViewSet)
router.register("providers/proxy", ProxyProviderViewSet) router.register("providers/proxy", ProxyProviderViewSet)
router.register("providers/oauth2", OAuth2ProviderViewSet) router.register("providers/oauth2", OAuth2ProviderViewSet)
router.register("providers/saml", SAMLProviderViewSet) router.register("providers/saml", SAMLProviderViewSet)

View File

@ -0,0 +1,20 @@
# Generated by Django 3.2 on 2021-04-26 09:27
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("authentik_outposts", "0015_auto_20201224_1206"),
]
operations = [
migrations.AlterField(
model_name="outpost",
name="type",
field=models.TextField(
choices=[("proxy", "Proxy"), ("ldap", "Ldap")], default="proxy"
),
),
]

View File

@ -0,0 +1,84 @@
# Generated by Django 3.2 on 2021-04-26 09:27
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("authentik_policies_event_matcher", "0012_auto_20210323_1339"),
]
operations = [
migrations.AlterField(
model_name="eventmatcherpolicy",
name="app",
field=models.TextField(
blank=True,
choices=[
("authentik.admin", "authentik Admin"),
("authentik.api", "authentik API"),
("authentik.events", "authentik Events"),
("authentik.crypto", "authentik Crypto"),
("authentik.flows", "authentik Flows"),
("authentik.outposts", "authentik Outpost"),
("authentik.lib", "authentik lib"),
("authentik.policies", "authentik Policies"),
("authentik.policies.dummy", "authentik Policies.Dummy"),
(
"authentik.policies.event_matcher",
"authentik Policies.Event Matcher",
),
("authentik.policies.expiry", "authentik Policies.Expiry"),
("authentik.policies.expression", "authentik Policies.Expression"),
("authentik.policies.hibp", "authentik Policies.HaveIBeenPwned"),
("authentik.policies.password", "authentik Policies.Password"),
("authentik.policies.reputation", "authentik Policies.Reputation"),
("authentik.providers.proxy", "authentik Providers.Proxy"),
("authentik.providers.ldap", "authentik Providers.LDAP"),
("authentik.providers.oauth2", "authentik Providers.OAuth2"),
("authentik.providers.saml", "authentik Providers.SAML"),
("authentik.recovery", "authentik Recovery"),
("authentik.sources.ldap", "authentik Sources.LDAP"),
("authentik.sources.oauth", "authentik Sources.OAuth"),
("authentik.sources.saml", "authentik Sources.SAML"),
(
"authentik.stages.authenticator_static",
"authentik Stages.Authenticator.Static",
),
(
"authentik.stages.authenticator_totp",
"authentik Stages.Authenticator.TOTP",
),
(
"authentik.stages.authenticator_validate",
"authentik Stages.Authenticator.Validate",
),
(
"authentik.stages.authenticator_webauthn",
"authentik Stages.Authenticator.WebAuthn",
),
("authentik.stages.captcha", "authentik Stages.Captcha"),
("authentik.stages.consent", "authentik Stages.Consent"),
("authentik.stages.deny", "authentik Stages.Deny"),
("authentik.stages.dummy", "authentik Stages.Dummy"),
("authentik.stages.email", "authentik Stages.Email"),
(
"authentik.stages.identification",
"authentik Stages.Identification",
),
("authentik.stages.invitation", "authentik Stages.User Invitation"),
("authentik.stages.password", "authentik Stages.Password"),
("authentik.stages.prompt", "authentik Stages.Prompt"),
("authentik.stages.user_delete", "authentik Stages.User Delete"),
("authentik.stages.user_login", "authentik Stages.User Login"),
("authentik.stages.user_logout", "authentik Stages.User Logout"),
("authentik.stages.user_write", "authentik Stages.User Write"),
("authentik.core", "authentik Core"),
("authentik.managed", "authentik Managed"),
],
default="",
help_text="Match events created by selected application. When left empty, all applications are matched.",
),
),
]

View File

View File

@ -0,0 +1,47 @@
"""LDAPProvider API Views"""
from rest_framework.fields import CharField
from rest_framework.serializers import ModelSerializer
from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet
from authentik.core.api.providers import ProviderSerializer
from authentik.providers.ldap.models import LDAPProvider
class LDAPProviderSerializer(ProviderSerializer):
"""LDAPProvider Serializer"""
class Meta:
model = LDAPProvider
fields = ProviderSerializer.Meta.fields + [
"bind_flow",
"base_dn",
]
class LDAPProviderViewSet(ModelViewSet):
"""LDAPProvider Viewset"""
queryset = LDAPProvider.objects.all()
serializer_class = LDAPProviderSerializer
ordering = ["name"]
class LDAPOutpostConfigSerializer(ModelSerializer):
"""LDAPProvider Serializer"""
application_slug = CharField(source="application.slug")
bind_flow_slug = CharField(source="bind_flow.slug")
class Meta:
model = LDAPProvider
fields = ["pk", "name", "base_dn", "bind_flow_slug", "application_slug"]
class LDAPOutpostConfigViewSet(ReadOnlyModelViewSet):
"""LDAPProvider Viewset"""
queryset = LDAPProvider.objects.filter(application__isnull=False)
serializer_class = LDAPOutpostConfigSerializer
ordering = ["name"]

View File

@ -0,0 +1,10 @@
"""authentik ldap provider app config"""
from django.apps import AppConfig
class AuthentikProviderLDAPConfig(AppConfig):
"""authentik ldap provider app config"""
name = "authentik.providers.ldap"
label = "authentik_providers_ldap"
verbose_name = "authentik Providers.LDAP"

View File

@ -0,0 +1,55 @@
# Generated by Django 3.2 on 2021-04-26 09:51
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
("authentik_core", "0019_source_managed"),
("authentik_flows", "0018_oob_flows"),
]
operations = [
migrations.CreateModel(
name="LDAPProvider",
fields=[
(
"provider_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="authentik_core.provider",
),
),
(
"base_dn",
models.TextField(
default="DC=ldap,DC=goauthentik,DC=io",
help_text="DN under which objects are accessible.",
),
),
(
"bind_flow",
models.ForeignKey(
default=None,
help_text="Flow which is used to bind users. When left empty, no users will be able to bind.",
null=True,
on_delete=django.db.models.deletion.SET_DEFAULT,
to="authentik_flows.flow",
),
),
],
options={
"verbose_name": "LDAP Provider",
"verbose_name_plural": "LDAP Providers",
},
bases=("authentik_core.provider",),
),
]

View File

@ -0,0 +1,51 @@
"""LDAP Provider"""
from typing import Optional, Type
from django.db import models
from django.utils.translation import gettext_lazy as _
from rest_framework.serializers import Serializer
from authentik.core.models import Provider
from authentik.flows.models import Flow
class LDAPProvider(Provider):
"""LDAP Provider"""
base_dn = models.TextField(
default="DC=ldap,DC=goauthentik,DC=io",
help_text=_("DN under which objects are accessible."),
)
bind_flow = models.ForeignKey(
Flow,
null=True,
default=None,
on_delete=models.SET_DEFAULT,
help_text=_(
"Flow which is used to bind users. When left empty, no users will be able to bind."
),
)
@property
def launch_url(self) -> Optional[str]:
"""LDAP never has a launch URL"""
return None
@property
def component(self) -> str:
return "ak-provider-ldap-form"
@property
def serializer(self) -> Type[Serializer]:
from authentik.providers.oauth2.api.provider import OAuth2ProviderSerializer
return OAuth2ProviderSerializer
def __str__(self):
return f"LDAP Provider {self.name}"
class Meta:
verbose_name = _("LDAP Provider")
verbose_name_plural = _("LDAP Providers")

View File

@ -1,11 +1,11 @@
"""authentik auth oauth provider app config""" """authentik oauth provider app config"""
from importlib import import_module from importlib import import_module
from django.apps import AppConfig from django.apps import AppConfig
class AuthentikProviderOAuth2Config(AppConfig): class AuthentikProviderOAuth2Config(AppConfig):
"""authentik auth oauth provider app config""" """authentik oauth provider app config"""
name = "authentik.providers.oauth2" name = "authentik.providers.oauth2"
label = "authentik_providers_oauth2" label = "authentik_providers_oauth2"

View File

@ -102,6 +102,7 @@ INSTALLED_APPS = [
"authentik.policies.password", "authentik.policies.password",
"authentik.policies.reputation", "authentik.policies.reputation",
"authentik.providers.proxy", "authentik.providers.proxy",
"authentik.providers.ldap",
"authentik.providers.oauth2", "authentik.providers.oauth2",
"authentik.providers.saml", "authentik.providers.saml",
"authentik.recovery", "authentik.recovery",

View File

@ -4671,6 +4671,103 @@ paths:
required: true required: true
type: string type: string
format: uuid format: uuid
/outposts/ldap/:
get:
operationId: outposts_ldap_list
description: LDAPProvider 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/LDAPOutpostConfig'
'403':
description: Authentication credentials were invalid, absent or insufficient.
schema:
$ref: '#/definitions/GenericError'
tags:
- outposts
parameters: []
/outposts/ldap/{id}/:
get:
operationId: outposts_ldap_read
description: LDAPProvider Viewset
parameters: []
responses:
'200':
description: ''
schema:
$ref: '#/definitions/LDAPOutpostConfig'
'403':
description: Authentication credentials were invalid, absent or insufficient.
schema:
$ref: '#/definitions/GenericError'
'404':
description: Object does not exist or caller has insufficient permissions
to access it.
schema:
$ref: '#/definitions/APIException'
tags:
- outposts
parameters:
- name: id
in: path
description: A unique integer value identifying this LDAP Provider.
required: true
type: integer
/outposts/outposts/: /outposts/outposts/:
get: get:
operationId: outposts_outposts_list operationId: outposts_outposts_list
@ -8739,6 +8836,203 @@ paths:
description: A unique integer value identifying this provider. description: A unique integer value identifying this provider.
required: true required: true
type: integer type: integer
/providers/ldap/:
get:
operationId: providers_ldap_list
description: LDAPProvider 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/LDAPProvider'
'403':
description: Authentication credentials were invalid, absent or insufficient.
schema:
$ref: '#/definitions/GenericError'
tags:
- providers
post:
operationId: providers_ldap_create
description: LDAPProvider Viewset
parameters:
- name: data
in: body
required: true
schema:
$ref: '#/definitions/LDAPProvider'
responses:
'201':
description: ''
schema:
$ref: '#/definitions/LDAPProvider'
'400':
description: Invalid input.
schema:
$ref: '#/definitions/ValidationError'
'403':
description: Authentication credentials were invalid, absent or insufficient.
schema:
$ref: '#/definitions/GenericError'
tags:
- providers
parameters: []
/providers/ldap/{id}/:
get:
operationId: providers_ldap_read
description: LDAPProvider Viewset
parameters: []
responses:
'200':
description: ''
schema:
$ref: '#/definitions/LDAPProvider'
'403':
description: Authentication credentials were invalid, absent or insufficient.
schema:
$ref: '#/definitions/GenericError'
'404':
description: Object does not exist or caller has insufficient permissions
to access it.
schema:
$ref: '#/definitions/APIException'
tags:
- providers
put:
operationId: providers_ldap_update
description: LDAPProvider Viewset
parameters:
- name: data
in: body
required: true
schema:
$ref: '#/definitions/LDAPProvider'
responses:
'200':
description: ''
schema:
$ref: '#/definitions/LDAPProvider'
'400':
description: Invalid input.
schema:
$ref: '#/definitions/ValidationError'
'403':
description: Authentication credentials were invalid, absent or insufficient.
schema:
$ref: '#/definitions/GenericError'
'404':
description: Object does not exist or caller has insufficient permissions
to access it.
schema:
$ref: '#/definitions/APIException'
tags:
- providers
patch:
operationId: providers_ldap_partial_update
description: LDAPProvider Viewset
parameters:
- name: data
in: body
required: true
schema:
$ref: '#/definitions/LDAPProvider'
responses:
'200':
description: ''
schema:
$ref: '#/definitions/LDAPProvider'
'400':
description: Invalid input.
schema:
$ref: '#/definitions/ValidationError'
'403':
description: Authentication credentials were invalid, absent or insufficient.
schema:
$ref: '#/definitions/GenericError'
'404':
description: Object does not exist or caller has insufficient permissions
to access it.
schema:
$ref: '#/definitions/APIException'
tags:
- providers
delete:
operationId: providers_ldap_delete
description: LDAPProvider Viewset
parameters: []
responses:
'204':
description: ''
'403':
description: Authentication credentials were invalid, absent or insufficient.
schema:
$ref: '#/definitions/GenericError'
'404':
description: Object does not exist or caller has insufficient permissions
to access it.
schema:
$ref: '#/definitions/APIException'
tags:
- providers
parameters:
- name: id
in: path
description: A unique integer value identifying this LDAP Provider.
required: true
type: integer
/providers/oauth2/: /providers/oauth2/:
get: get:
operationId: providers_oauth2_list operationId: providers_oauth2_list
@ -15718,6 +16012,34 @@ definitions:
title: Version outdated title: Version outdated
type: boolean type: boolean
readOnly: true readOnly: true
LDAPOutpostConfig:
required:
- name
- bind_flow_slug
- application_slug
type: object
properties:
pk:
title: ID
type: integer
readOnly: true
name:
title: Name
type: string
minLength: 1
base_dn:
title: Base dn
description: DN under which objects are accessible.
type: string
minLength: 1
bind_flow_slug:
title: Bind flow slug
type: string
minLength: 1
application_slug:
title: Application slug
type: string
minLength: 1
OpenIDConnectConfiguration: OpenIDConnectConfiguration:
description: Embed OpenID Connect provider information description: Embed OpenID Connect provider information
required: required:
@ -16223,6 +16545,7 @@ definitions:
- authentik.policies.password - authentik.policies.password
- authentik.policies.reputation - authentik.policies.reputation
- authentik.providers.proxy - authentik.providers.proxy
- authentik.providers.ldap
- authentik.providers.oauth2 - authentik.providers.oauth2
- authentik.providers.saml - authentik.providers.saml
- authentik.recovery - authentik.recovery
@ -16727,6 +17050,63 @@ definitions:
description: Description shown to the user when consenting. If left empty, description: Description shown to the user when consenting. If left empty,
the user won't be informed. the user won't be informed.
type: string type: string
LDAPProvider:
required:
- name
- authorization_flow
type: object
properties:
pk:
title: ID
type: integer
readOnly: true
name:
title: Name
type: string
minLength: 1
authorization_flow:
title: Authorization flow
description: Flow used when authorizing this provider.
type: string
format: uuid
property_mappings:
type: array
items:
type: string
format: uuid
uniqueItems: true
component:
title: Component
type: string
readOnly: true
assigned_application_slug:
title: Assigned application slug
type: string
readOnly: true
assigned_application_name:
title: Assigned application name
type: string
readOnly: true
verbose_name:
title: Verbose name
type: string
readOnly: true
verbose_name_plural:
title: Verbose name plural
type: string
readOnly: true
bind_flow:
title: Bind flow
description: Flow which is used to bind users. When left empty, no users will
be able to bind.
type: string
format: uuid
x-nullable: true
base_dn:
title: Base dn
description: DN under which objects are accessible.
type: string
minLength: 1
OAuth2ProviderSetupURLs: OAuth2ProviderSetupURLs:
type: object type: object
properties: properties: