Merge branch 'ldap-full-admin-dn' into 'master'

Ldap full admin dn

Closes #528

See merge request framasoft/mobilizon!733
This commit is contained in:
Thomas Citharel 2020-12-01 09:32:18 +01:00
commit def6df690d
2 changed files with 43 additions and 7 deletions

View file

@ -146,6 +146,10 @@ config :mobilizon, :ldap,
base: System.get_env("LDAP_BASE") || "dc=example,dc=com", base: System.get_env("LDAP_BASE") || "dc=example,dc=com",
uid: System.get_env("LDAP_UID") || "cn", uid: System.get_env("LDAP_UID") || "cn",
require_bind_for_search: !(System.get_env("LDAP_REQUIRE_BIND_FOR_SEARCH") == "false"), require_bind_for_search: !(System.get_env("LDAP_REQUIRE_BIND_FOR_SEARCH") == "false"),
# The full CN to filter by `memberOf`, or `false` if disabled
group: false,
# Either the admin UID matching the field in `uid`,
# Either a tuple with the fully qualified DN: {:full, uid=admin,dc=example.com,dc=local}
bind_uid: System.get_env("LDAP_BIND_UID"), bind_uid: System.get_env("LDAP_BIND_UID"),
bind_password: System.get_env("LDAP_BIND_PASSWORD") bind_password: System.get_env("LDAP_BIND_PASSWORD")

View file

@ -61,9 +61,11 @@ defmodule Mobilizon.Service.Auth.LDAPAuthenticator do
base = Keyword.get(ldap, :base) base = Keyword.get(ldap, :base)
uid_field = Keyword.get(ldap, :uid, "cn") uid_field = Keyword.get(ldap, :uid, "cn")
group = Keyword.get(ldap, :group, false)
# We first need to find the LDAP UID/CN for this specif email # We first need to find the LDAP UID/CN for this specif email
with uid when is_binary(uid) <- search_user(connection, ldap, base, uid_field, email), with uid when is_binary(uid) <-
search_user(connection, ldap, base, uid_field, email, group),
# Then we can verify the user's password # Then we can verify the user's password
:ok <- bind_user(connection, base, uid_field, uid, password) do :ok <- bind_user(connection, base, uid_field, uid, password) do
case fetch_user(email) do case fetch_user(email) do
@ -97,6 +99,15 @@ defmodule Mobilizon.Service.Auth.LDAPAuthenticator do
end end
end end
# Bind user with full DN
@spec bind_user(any(), String.t(), String.t(), {:full, String.t()}, String.t()) ::
User.t() | any()
defp bind_user(connection, _base, _uid, {:full, field}, password) do
Logger.debug("Binding to LDAP with \"#{field}\"")
:eldap.simple_bind(connection, field, password)
end
# Bind user with only uid field on top of base
@spec bind_user(any(), String.t(), String.t(), String.t(), String.t()) :: @spec bind_user(any(), String.t(), String.t(), String.t(), String.t()) ::
User.t() | any() User.t() | any()
defp bind_user(connection, base, uid, field, password) do defp bind_user(connection, base, uid, field, password) do
@ -105,9 +116,16 @@ defmodule Mobilizon.Service.Auth.LDAPAuthenticator do
:eldap.simple_bind(connection, bind, password) :eldap.simple_bind(connection, bind, password)
end end
@spec search_user(any(), Keyword.t(), String.t(), String.t(), String.t()) :: @spec search_user(
any(),
Keyword.t(),
String.t(),
String.t(),
String.t(),
String.t() | boolean()
) ::
String.t() | {:error, :ldap_registration_missing_attributes} | any() String.t() | {:error, :ldap_registration_missing_attributes} | any()
defp search_user(connection, ldap, base, uid, email) do defp search_user(connection, ldap, base, uid, email, group) do
# We may need to bind before performing the search # We may need to bind before performing the search
res = res =
if Keyword.get(ldap, :require_bind_for_search, true) do if Keyword.get(ldap, :require_bind_for_search, true) do
@ -119,20 +137,20 @@ defmodule Mobilizon.Service.Auth.LDAPAuthenticator do
end end
if res == :ok do if res == :ok do
do_search_user(connection, base, uid, email) do_search_user(connection, base, uid, email, group)
else else
res res
end end
end end
# Search an user by uid to find their CN # Search an user by uid to find their CN
@spec do_search_user(any(), String.t(), String.t(), String.t()) :: @spec do_search_user(any(), String.t(), String.t(), String.t(), String.t() | boolean()) ::
String.t() | {:error, :ldap_registration_missing_attributes} | any() String.t() | {:error, :ldap_registration_missing_attributes} | any()
defp do_search_user(connection, base, uid, email) do defp do_search_user(connection, base, uid, email, group) do
with {:ok, {:eldap_search_result, [{:eldap_entry, _, attributes}], _}} <- with {:ok, {:eldap_search_result, [{:eldap_entry, _, attributes}], _}} <-
:eldap.search(connection, [ :eldap.search(connection, [
{:base, to_charlist(base)}, {:base, to_charlist(base)},
{:filter, :eldap.equalityMatch(to_charlist("mail"), to_charlist(email))}, {:filter, search_filter(email, group)},
{:scope, :eldap.wholeSubtree()}, {:scope, :eldap.wholeSubtree()},
{:attributes, [to_charlist(uid)]}, {:attributes, [to_charlist(uid)]},
{:timeout, @search_timeout} {:timeout, @search_timeout}
@ -153,6 +171,20 @@ defmodule Mobilizon.Service.Auth.LDAPAuthenticator do
end end
end end
@spec search_filter(String.t(), boolean()) :: any()
defp search_filter(email, false) do
:eldap.equalityMatch('mail', to_charlist(email))
end
# If we need to filter for group memberships as well
@spec search_filter(String.t(), String.t()) :: any()
defp search_filter(email, group) when is_binary(group) do
:eldap.and([
:eldap.equalityMatch('mail', to_charlist(email)),
:eldap.equalityMatch('memberOf', to_charlist(group))
])
end
@spec register_user(String.t()) :: User.t() | any() @spec register_user(String.t()) :: User.t() | any()
defp register_user(email) do defp register_user(email) do
case Users.create_external(email, "ldap") do case Users.create_external(email, "ldap") do