Skip to content
Snippets Groups Projects
Commit e278d470 authored by link0ff's avatar link0ff
Browse files

OpenLDAP support

parent 5a4e2905
No related branches found
No related tags found
No related merge requests found
...@@ -344,6 +344,15 @@ config :pleroma, Pleroma.Jobs, ...@@ -344,6 +344,15 @@ config :pleroma, Pleroma.Jobs,
federator_outgoing: [max_jobs: 50], federator_outgoing: [max_jobs: 50],
mailer: [max_jobs: 10] mailer: [max_jobs: 10]
config :pleroma, :ldap,
enabled: System.get_env("LDAP_ENABLED") == "true",
host: System.get_env("LDAP_HOST") || "localhost",
port: String.to_integer(System.get_env("LDAP_PORT") || "389"),
ssl: System.get_env("LDAP_SSL") == "true",
sslopts: [],
base: System.get_env("LDAP_BASE") || "dc=example,dc=com",
uid: System.get_env("LDAP_UID") || "cn"
# Import environment specific config. This must remain at the bottom # Import environment specific config. This must remain at the bottom
# of this file so it overrides the configuration defined above. # of this file so it overrides the configuration defined above.
import_config "#{Mix.env()}.exs" import_config "#{Mix.env()}.exs"
...@@ -301,3 +301,12 @@ For each pool, the options are: ...@@ -301,3 +301,12 @@ For each pool, the options are:
* `max_connections` - how much connections a pool can hold * `max_connections` - how much connections a pool can hold
* `timeout` - retention duration for connections * `timeout` - retention duration for connections
## :ldap
* `enabled`: enables LDAP authentication
* `host`: LDAP server hostname
* `port`: LDAP port, e.g. 389 or 636
* `ssl`: true to use SSL
* `sslopts`: additional SSL options
* `base`: LDAP base, e.g. "dc=example,dc=com"
* `uid`: attribute type to authenticate the user, e.g. when "cn", the filter will be "cn=username,base"
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.LDAP do
alias Pleroma.User
require Logger
@connection_timeout 10_000
@search_timeout 10_000
def get_user(name, password) do
ldap = Pleroma.Config.get(:ldap, [])
host = Keyword.get(ldap, :host, "localhost")
port = Keyword.get(ldap, :port, 389)
ssl = Keyword.get(ldap, :ssl, false)
sslopts = Keyword.get(ldap, :sslopts, [])
options =
[{:port, port}, {:ssl, ssl}, {:timeout, @connection_timeout}] ++
if sslopts != [], do: [{:sslopts, sslopts}], else: []
case :eldap.open([to_charlist(host)], options) do
{:ok, connection} ->
try do
uid = Keyword.get(ldap, :uid, "cn")
base = Keyword.get(ldap, :base)
case :eldap.simple_bind(connection, "#{uid}=#{name},#{base}", password) do
:ok ->
case User.get_by_nickname_or_email(name) do
%User{} = user ->
user
_ ->
register_user(connection, base, uid, name, password)
end
error ->
error
end
after
:eldap.close(connection)
end
{:error, error} ->
Logger.error("Could not open LDAP connection: #{inspect(error)}")
{:error, {:ldap_connection_error, error}}
end
end
def register_user(connection, base, uid, name, password) do
case :eldap.search(connection, [
{:base, to_charlist(base)},
{:filter, :eldap.equalityMatch(to_charlist(uid), to_charlist(name))},
{:scope, :eldap.wholeSubtree()},
{:timeout, @search_timeout}
]) do
{:ok, {:eldap_search_result, [{:eldap_entry, _, attributes}], _}} ->
with {_, [mail]} <- List.keyfind(attributes, 'mail', 0) do
params = %{
email: :erlang.list_to_binary(mail),
name: name,
nickname: name,
password: password,
password_confirmation: password
}
changeset = User.register_changeset(%User{}, params)
case User.register(changeset) do
{:ok, user} -> user
error -> error
end
else
_ -> {:error, :ldap_registration_missing_attributes}
end
error ->
error
end
end
end
...@@ -130,8 +130,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do ...@@ -130,8 +130,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
%{"grant_type" => "password", "username" => name, "password" => password} = params %{"grant_type" => "password", "username" => name, "password" => password} = params
) do ) do
with %App{} = app <- get_app_from_request(conn, params), with %App{} = app <- get_app_from_request(conn, params),
%User{} = user <- User.get_by_nickname_or_email(name), %User{} = user <- get_user(name, password),
true <- Pbkdf2.checkpw(password, user.password_hash),
{:auth_active, true} <- {:auth_active, User.auth_active?(user)}, {:auth_active, true} <- {:auth_active, User.auth_active?(user)},
scopes <- oauth_scopes(params, app.scopes), scopes <- oauth_scopes(params, app.scopes),
[] <- scopes -- app.scopes, [] <- scopes -- app.scopes,
...@@ -215,4 +214,28 @@ defmodule Pleroma.Web.OAuth.OAuthController do ...@@ -215,4 +214,28 @@ defmodule Pleroma.Web.OAuth.OAuthController do
nil nil
end end
end end
defp get_user(name, password) do
if Pleroma.Config.get([:ldap, :enabled]) do
case Pleroma.LDAP.get_user(name, password) do
%User{} = user ->
user
{:error, {:ldap_connection_error, _}} ->
# When LDAP is unavailable, try default login
with %User{} = user <- User.get_by_nickname_or_email(name),
true <- Pbkdf2.checkpw(password, user.password_hash) do
user
end
error ->
error
end
else
with %User{} = user <- User.get_by_nickname_or_email(name),
true <- Pbkdf2.checkpw(password, user.password_hash) do
user
end
end
end
end end
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment