Merge remote-tracking branch 'origin/develop' into fork
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
commit
aeac257cf2
25 changed files with 344 additions and 183 deletions
|
@ -1,8 +1,8 @@
|
|||
image: git.pleroma.social:5050/pleroma/pleroma/ci-base:elixir-1.13.4-otp-25
|
||||
image: git.pleroma.social:5050/pleroma/pleroma/ci-base:elixir-1.14.5-otp-25
|
||||
|
||||
variables: &global_variables
|
||||
# Only used for the release
|
||||
ELIXIR_VER: 1.13.4
|
||||
ELIXIR_VER: 1.14.5
|
||||
POSTGRES_DB: pleroma_test
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
|
@ -71,7 +71,7 @@ check-changelog:
|
|||
tags:
|
||||
- amd64
|
||||
|
||||
build-1.13.4-otp-25:
|
||||
build-1.14.5-otp-25:
|
||||
extends:
|
||||
- .build_changes_policy
|
||||
- .using-ci-base
|
||||
|
@ -119,7 +119,7 @@ benchmark:
|
|||
- mix ecto.migrate
|
||||
- mix pleroma.load_testing
|
||||
|
||||
unit-testing-1.13.4-otp-25:
|
||||
unit-testing-1.14.5-otp-25:
|
||||
extends:
|
||||
- .build_changes_policy
|
||||
- .using-ci-base
|
||||
|
@ -134,7 +134,7 @@ unit-testing-1.13.4-otp-25:
|
|||
script: &testing_script
|
||||
- mix ecto.create
|
||||
- mix ecto.migrate
|
||||
- mix test --cover --preload-modules
|
||||
- mix pleroma.test_runner --cover --preload-modules
|
||||
coverage: '/^Line total: ([^ ]*%)$/'
|
||||
artifacts:
|
||||
reports:
|
||||
|
|
1
changelog.d/elixir.change
Normal file
1
changelog.d/elixir.change
Normal file
|
@ -0,0 +1 @@
|
|||
Elixir 1.14 and Erlang/OTP 23 is now the minimum supported release
|
1
changelog.d/ldap-ca.add
Normal file
1
changelog.d/ldap-ca.add
Normal file
|
@ -0,0 +1 @@
|
|||
LDAP configuration now permits overriding the CA root certificate file for TLS validation.
|
1
changelog.d/ldap-refactor.change
Normal file
1
changelog.d/ldap-refactor.change
Normal file
|
@ -0,0 +1 @@
|
|||
LDAP authentication has been refactored to operate as a GenServer process which will maintain an active connection to the LDAP server.
|
1
changelog.d/ldaps.fix
Normal file
1
changelog.d/ldaps.fix
Normal file
|
@ -0,0 +1 @@
|
|||
LDAPS connections (implicit TLS) are now supported.
|
1
changelog.d/oban-update.change
Normal file
1
changelog.d/oban-update.change
Normal file
|
@ -0,0 +1 @@
|
|||
Oban updated to 2.18.3
|
1
changelog.d/swoosh-mua.add
Normal file
1
changelog.d/swoosh-mua.add
Normal file
|
@ -0,0 +1 @@
|
|||
Added dependencies for Swoosh's Mua mail adapter
|
|
@ -1 +0,0 @@
|
|||
docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t git.pleroma.social:5050/pleroma/pleroma/ci-base:elixir-1.12 --push .
|
|
@ -1,8 +0,0 @@
|
|||
FROM elixir:1.13.4-otp-25
|
||||
|
||||
# Single RUN statement, otherwise intermediate images are created
|
||||
# https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#run
|
||||
RUN apt-get update &&\
|
||||
apt-get install -y libmagic-dev cmake libimage-exiftool-perl ffmpeg &&\
|
||||
mix local.hex --force &&\
|
||||
mix local.rebar --force
|
|
@ -1,4 +1,4 @@
|
|||
FROM elixir:1.12.3
|
||||
FROM elixir:1.14.5-otp-25
|
||||
|
||||
# Single RUN statement, otherwise intermediate images are created
|
||||
# https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#run
|
|
@ -1 +1 @@
|
|||
docker buildx build --platform linux/amd64,linux/arm64 -t git.pleroma.social:5050/pleroma/pleroma/ci-base:elixir-1.13.4-otp-25 --push .
|
||||
docker buildx build --platform linux/amd64,linux/arm64 -t git.pleroma.social:5050/pleroma/pleroma/ci-base:elixir-1.14.5-otp-25 --push .
|
|
@ -625,14 +625,17 @@
|
|||
|
||||
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"),
|
||||
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: [],
|
||||
tls: System.get_env("LDAP_TLS") == "true",
|
||||
tlsopts: [],
|
||||
base: System.get_env("LDAP_BASE") || "dc=example,dc=com",
|
||||
uid: System.get_env("LDAP_UID") || "cn"
|
||||
base: System.get_env("LDAP_BASE", "dc=example,dc=com"),
|
||||
uid: System.get_env("LDAP_UID", "cn"),
|
||||
# defaults to CAStore's Mozilla roots
|
||||
cacertfile: System.get_env("LDAP_CACERTFILE", nil),
|
||||
mail: System.get_env("LDAP_MAIL", "mail")
|
||||
|
||||
oauth_consumer_strategies =
|
||||
System.get_env("OAUTH_CONSUMER_STRATEGIES")
|
||||
|
|
|
@ -2299,14 +2299,8 @@
|
|||
label: "SSL options",
|
||||
type: :keyword,
|
||||
description: "Additional SSL options",
|
||||
suggestions: [cacertfile: "path/to/file/with/PEM/cacerts", verify: :verify_peer],
|
||||
suggestions: [verify: :verify_peer],
|
||||
children: [
|
||||
%{
|
||||
key: :cacertfile,
|
||||
type: :string,
|
||||
description: "Path to file with PEM encoded cacerts",
|
||||
suggestions: ["path/to/file/with/PEM/cacerts"]
|
||||
},
|
||||
%{
|
||||
key: :verify,
|
||||
type: :atom,
|
||||
|
@ -2326,14 +2320,8 @@
|
|||
label: "TLS options",
|
||||
type: :keyword,
|
||||
description: "Additional TLS options",
|
||||
suggestions: [cacertfile: "path/to/file/with/PEM/cacerts", verify: :verify_peer],
|
||||
suggestions: [verify: :verify_peer],
|
||||
children: [
|
||||
%{
|
||||
key: :cacertfile,
|
||||
type: :string,
|
||||
description: "Path to file with PEM encoded cacerts",
|
||||
suggestions: ["path/to/file/with/PEM/cacerts"]
|
||||
},
|
||||
%{
|
||||
key: :verify,
|
||||
type: :atom,
|
||||
|
@ -2350,11 +2338,25 @@
|
|||
},
|
||||
%{
|
||||
key: :uid,
|
||||
label: "UID",
|
||||
label: "UID Attribute",
|
||||
type: :string,
|
||||
description:
|
||||
"LDAP attribute name to authenticate the user, e.g. when \"cn\", the filter will be \"cn=username,base\"",
|
||||
suggestions: ["cn"]
|
||||
},
|
||||
%{
|
||||
key: :cacertfile,
|
||||
label: "CACertfile",
|
||||
type: :string,
|
||||
description: "Path to CA certificate file"
|
||||
},
|
||||
%{
|
||||
key: :mail,
|
||||
label: "Mail Attribute",
|
||||
type: :string,
|
||||
description:
|
||||
"LDAP attribute name to use as the email address when automatically registering the user on first login",
|
||||
suggestions: ["mail"]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
@ -742,6 +742,21 @@ config :pleroma, Pleroma.Emails.Mailer,
|
|||
auth: :always
|
||||
```
|
||||
|
||||
An example for Mua adapter:
|
||||
|
||||
```elixir
|
||||
config :pleroma, Pleroma.Emails.Mailer,
|
||||
enabled: true,
|
||||
adapter: Swoosh.Adapters.Mua,
|
||||
relay: "mail.example.com",
|
||||
port: 465,
|
||||
auth: [
|
||||
username: "YOUR_USERNAME@domain.tld",
|
||||
password: "YOUR_SMTP_PASSWORD"
|
||||
],
|
||||
protocol: :ssl
|
||||
```
|
||||
|
||||
### :email_notifications
|
||||
|
||||
Email notifications settings.
|
||||
|
@ -968,12 +983,13 @@ Pleroma account will be created with the same name as the LDAP user name.
|
|||
* `enabled`: enables LDAP authentication
|
||||
* `host`: LDAP server hostname
|
||||
* `port`: LDAP port, e.g. 389 or 636
|
||||
* `ssl`: true to use SSL, usually implies the port 636
|
||||
* `ssl`: true to use implicit SSL/TLS, usually port 636
|
||||
* `sslopts`: additional SSL options
|
||||
* `tls`: true to start TLS, usually implies the port 389
|
||||
* `tls`: true to use explicit TLS (STARTTLS), usually port 389
|
||||
* `tlsopts`: additional TLS options
|
||||
* `base`: LDAP base, e.g. "dc=example,dc=com"
|
||||
* `uid`: LDAP attribute name to authenticate the user, e.g. when "cn", the filter will be "cn=username,base"
|
||||
* `cacertfile`: Path to alternate CA root certificates file
|
||||
|
||||
Note, if your LDAP server is an Active Directory server the correct value is commonly `uid: "cn"`, but if you use an
|
||||
OpenLDAP server the value may be `uid: "uid"`.
|
||||
|
|
|
@ -14,7 +14,7 @@ Note: This article is potentially outdated because at this time we may not have
|
|||
|
||||
- PostgreSQL 11.0以上 (Ubuntu16.04では9.5しか提供されていないので,[](https://www.postgresql.org/download/linux/ubuntu/)こちらから新しいバージョンを入手してください)
|
||||
- `postgresql-contrib` 11.0以上 (同上)
|
||||
- Elixir 1.13 以上 ([Debianのリポジトリからインストールしないこと!!! ここからインストールすること!](https://elixir-lang.org/install.html#unix-and-unix-like)。または [asdf](https://github.com/asdf-vm/asdf) をpleromaユーザーでインストールしてください)
|
||||
- Elixir 1.14 以上 ([Debianのリポジトリからインストールしないこと!!! ここからインストールすること!](https://elixir-lang.org/install.html#unix-and-unix-like)。または [asdf](https://github.com/asdf-vm/asdf) をpleromaユーザーでインストールしてください)
|
||||
- `erlang-dev`
|
||||
- `erlang-nox`
|
||||
- `git`
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
## Required dependencies
|
||||
|
||||
* PostgreSQL >=11.0
|
||||
* Elixir >=1.13.0 <1.17
|
||||
* Erlang OTP >=22.2.0 (supported: <27)
|
||||
* Elixir >=1.14.0 <1.17
|
||||
* Erlang OTP >=23.0.0 (supported: <27)
|
||||
* git
|
||||
* file / libmagic
|
||||
* gcc or clang
|
||||
|
|
25
lib/mix/tasks/pleroma/test_runner.ex
Normal file
25
lib/mix/tasks/pleroma/test_runner.ex
Normal file
|
@ -0,0 +1,25 @@
|
|||
defmodule Mix.Tasks.Pleroma.TestRunner do
|
||||
@shortdoc "Retries tests once if they fail"
|
||||
|
||||
use Mix.Task
|
||||
|
||||
def run(args \\ []) do
|
||||
case System.cmd("mix", ["test"] ++ args, into: IO.stream(:stdio, :line)) do
|
||||
{_, 0} ->
|
||||
:ok
|
||||
|
||||
_ ->
|
||||
retry(args)
|
||||
end
|
||||
end
|
||||
|
||||
def retry(args) do
|
||||
case System.cmd("mix", ["test", "--failed"] ++ args, into: IO.stream(:stdio, :line)) do
|
||||
{_, 0} ->
|
||||
:ok
|
||||
|
||||
_ ->
|
||||
exit(1)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -97,6 +97,7 @@ def start(_type, _args) do
|
|||
children =
|
||||
[
|
||||
Pleroma.PromEx,
|
||||
Pleroma.LDAP,
|
||||
Pleroma.Repo,
|
||||
Config.TransferTask,
|
||||
Pleroma.Emoji,
|
||||
|
|
|
@ -21,7 +21,8 @@ defp reboot_time_keys,
|
|||
{:pleroma, :markup},
|
||||
{:pleroma, :streamer},
|
||||
{:pleroma, :pools},
|
||||
{:pleroma, :connections_pool}
|
||||
{:pleroma, :connections_pool},
|
||||
{:pleroma, :ldap}
|
||||
]
|
||||
|
||||
defp reboot_time_subkeys,
|
||||
|
|
245
lib/pleroma/ldap.ex
Normal file
245
lib/pleroma/ldap.ex
Normal file
|
@ -0,0 +1,245 @@
|
|||
defmodule Pleroma.LDAP do
|
||||
use GenServer
|
||||
|
||||
require Logger
|
||||
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.User
|
||||
|
||||
import Pleroma.Web.Auth.Helpers, only: [fetch_user: 1]
|
||||
|
||||
@connection_timeout 2_000
|
||||
@search_timeout 2_000
|
||||
|
||||
def start_link(_) do
|
||||
GenServer.start_link(__MODULE__, [], name: __MODULE__)
|
||||
end
|
||||
|
||||
@impl true
|
||||
def init(state) do
|
||||
case {Config.get(Pleroma.Web.Auth.Authenticator), Config.get([:ldap, :enabled])} do
|
||||
{Pleroma.Web.Auth.LDAPAuthenticator, true} ->
|
||||
{:ok, state, {:continue, :connect}}
|
||||
|
||||
{Pleroma.Web.Auth.LDAPAuthenticator, false} ->
|
||||
Logger.error(
|
||||
"LDAP Authenticator enabled but :pleroma, :ldap is not enabled. Auth will not work."
|
||||
)
|
||||
|
||||
{:ok, state}
|
||||
|
||||
{_, true} ->
|
||||
Logger.warning(
|
||||
":pleroma, :ldap is enabled but Pleroma.Web.Authenticator is not set to the LDAPAuthenticator. LDAP will not be used."
|
||||
)
|
||||
|
||||
{:ok, state}
|
||||
|
||||
_ ->
|
||||
{:ok, state}
|
||||
end
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_continue(:connect, _state), do: do_handle_connect()
|
||||
|
||||
@impl true
|
||||
def handle_info(:connect, _state), do: do_handle_connect()
|
||||
|
||||
def handle_info({:bind_after_reconnect, name, password, from}, state) do
|
||||
result = bind_user(state[:handle], name, password)
|
||||
|
||||
GenServer.reply(from, result)
|
||||
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
defp do_handle_connect do
|
||||
state =
|
||||
case connect() do
|
||||
{:ok, handle} ->
|
||||
:eldap.controlling_process(handle, self())
|
||||
Process.link(handle)
|
||||
[handle: handle]
|
||||
|
||||
_ ->
|
||||
Logger.error("Failed to connect to LDAP. Retrying in 5000ms")
|
||||
Process.send_after(self(), :connect, 5_000)
|
||||
[]
|
||||
end
|
||||
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_call({:bind_user, name, password}, from, state) do
|
||||
case bind_user(state[:handle], name, password) do
|
||||
:needs_reconnect ->
|
||||
Process.send(self(), {:bind_after_reconnect, name, password, from}, [])
|
||||
{:noreply, state, {:continue, :connect}}
|
||||
|
||||
result ->
|
||||
{:reply, result, state, :hibernate}
|
||||
end
|
||||
end
|
||||
|
||||
@impl true
|
||||
def terminate(_, state) do
|
||||
handle = Keyword.get(state, :handle)
|
||||
|
||||
if not is_nil(handle) do
|
||||
:eldap.close(handle)
|
||||
end
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
def bind_user(name, password) do
|
||||
GenServer.call(__MODULE__, {:bind_user, name, password})
|
||||
end
|
||||
|
||||
defp connect do
|
||||
ldap = Config.get(:ldap, [])
|
||||
host = Keyword.get(ldap, :host, "localhost")
|
||||
port = Keyword.get(ldap, :port, 389)
|
||||
ssl = Keyword.get(ldap, :ssl, false)
|
||||
tls = Keyword.get(ldap, :tls, false)
|
||||
cacertfile = Keyword.get(ldap, :cacertfile) || CAStore.file_path()
|
||||
|
||||
if ssl, do: Application.ensure_all_started(:ssl)
|
||||
|
||||
default_secure_opts = [
|
||||
verify: :verify_peer,
|
||||
cacerts: decode_certfile(cacertfile),
|
||||
customize_hostname_check: [
|
||||
fqdn_fun: fn _ -> to_charlist(host) end
|
||||
]
|
||||
]
|
||||
|
||||
sslopts = Keyword.merge(default_secure_opts, Keyword.get(ldap, :sslopts, []))
|
||||
tlsopts = Keyword.merge(default_secure_opts, Keyword.get(ldap, :tlsopts, []))
|
||||
|
||||
default_options = [{:port, port}, {:ssl, ssl}, {:timeout, @connection_timeout}]
|
||||
|
||||
# :sslopts can only be included in :eldap.open/2 when {ssl: true}
|
||||
# or the connection will fail
|
||||
options =
|
||||
if ssl do
|
||||
default_options ++ [{:sslopts, sslopts}]
|
||||
else
|
||||
default_options
|
||||
end
|
||||
|
||||
case :eldap.open([to_charlist(host)], options) do
|
||||
{:ok, handle} ->
|
||||
try do
|
||||
cond do
|
||||
tls ->
|
||||
case :eldap.start_tls(
|
||||
handle,
|
||||
tlsopts,
|
||||
@connection_timeout
|
||||
) do
|
||||
:ok ->
|
||||
{:ok, handle}
|
||||
|
||||
error ->
|
||||
Logger.error("Could not start TLS: #{inspect(error)}")
|
||||
:eldap.close(handle)
|
||||
end
|
||||
|
||||
true ->
|
||||
{:ok, handle}
|
||||
end
|
||||
after
|
||||
:ok
|
||||
end
|
||||
|
||||
{:error, error} ->
|
||||
Logger.error("Could not open LDAP connection: #{inspect(error)}")
|
||||
{:error, {:ldap_connection_error, error}}
|
||||
end
|
||||
end
|
||||
|
||||
defp bind_user(handle, name, password) do
|
||||
uid = Config.get([:ldap, :uid], "cn")
|
||||
base = Config.get([:ldap, :base])
|
||||
|
||||
case :eldap.simple_bind(handle, "#{uid}=#{name},#{base}", password) do
|
||||
:ok ->
|
||||
case fetch_user(name) do
|
||||
%User{} = user ->
|
||||
user
|
||||
|
||||
_ ->
|
||||
register_user(handle, base, uid, name)
|
||||
end
|
||||
|
||||
# eldap does not inform us of socket closure
|
||||
# until it is used
|
||||
{:error, {:gen_tcp_error, :closed}} ->
|
||||
:eldap.close(handle)
|
||||
:needs_reconnect
|
||||
|
||||
{:error, error} = e ->
|
||||
Logger.error("Could not bind LDAP user #{name}: #{inspect(error)}")
|
||||
e
|
||||
end
|
||||
end
|
||||
|
||||
defp register_user(handle, base, uid, name) do
|
||||
case :eldap.search(handle, [
|
||||
{:base, to_charlist(base)},
|
||||
{:filter, :eldap.equalityMatch(to_charlist(uid), to_charlist(name))},
|
||||
{:scope, :eldap.wholeSubtree()},
|
||||
{:timeout, @search_timeout}
|
||||
]) do
|
||||
# The :eldap_search_result record structure changed in OTP 24.3 and added a controls field
|
||||
# https://github.com/erlang/otp/pull/5538
|
||||
{:ok, {:eldap_search_result, [{:eldap_entry, _object, attributes}], _referrals}} ->
|
||||
try_register(name, attributes)
|
||||
|
||||
{:ok, {:eldap_search_result, [{:eldap_entry, _object, attributes}], _referrals, _controls}} ->
|
||||
try_register(name, attributes)
|
||||
|
||||
error ->
|
||||
Logger.error("Couldn't register user because LDAP search failed: #{inspect(error)}")
|
||||
{:error, {:ldap_search_error, error}}
|
||||
end
|
||||
end
|
||||
|
||||
defp try_register(name, attributes) do
|
||||
mail_attribute = Config.get([:ldap, :mail])
|
||||
|
||||
params = %{
|
||||
name: name,
|
||||
nickname: name,
|
||||
password: nil
|
||||
}
|
||||
|
||||
params =
|
||||
case List.keyfind(attributes, to_charlist(mail_attribute), 0) do
|
||||
{_, [mail]} -> Map.put_new(params, :email, :erlang.list_to_binary(mail))
|
||||
_ -> params
|
||||
end
|
||||
|
||||
changeset = User.register_changeset_ldap(%User{}, params)
|
||||
|
||||
case User.register(changeset) do
|
||||
{:ok, user} -> user
|
||||
error -> error
|
||||
end
|
||||
end
|
||||
|
||||
defp decode_certfile(file) do
|
||||
with {:ok, data} <- File.read(file) do
|
||||
data
|
||||
|> :public_key.pem_decode()
|
||||
|> Enum.map(fn {_, b, _} -> b end)
|
||||
else
|
||||
_ ->
|
||||
Logger.error("Unable to read certfile: #{file}")
|
||||
[]
|
||||
end
|
||||
end
|
||||
end
|
|
@ -3,18 +3,14 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.Auth.LDAPAuthenticator do
|
||||
alias Pleroma.LDAP
|
||||
alias Pleroma.User
|
||||
|
||||
require Logger
|
||||
|
||||
import Pleroma.Web.Auth.Helpers, only: [fetch_credentials: 1, fetch_user: 1]
|
||||
import Pleroma.Web.Auth.Helpers, only: [fetch_credentials: 1]
|
||||
|
||||
@behaviour Pleroma.Web.Auth.Authenticator
|
||||
@base Pleroma.Web.Auth.PleromaAuthenticator
|
||||
|
||||
@connection_timeout 10_000
|
||||
@search_timeout 10_000
|
||||
|
||||
defdelegate get_registration(conn), to: @base
|
||||
defdelegate create_from_registration(conn, registration), to: @base
|
||||
defdelegate handle_error(conn, error), to: @base
|
||||
|
@ -24,7 +20,7 @@ defmodule Pleroma.Web.Auth.LDAPAuthenticator do
|
|||
def get_user(%Plug.Conn{} = conn) do
|
||||
with {:ldap, true} <- {:ldap, Pleroma.Config.get([:ldap, :enabled])},
|
||||
{:ok, {name, password}} <- fetch_credentials(conn),
|
||||
%User{} = user <- ldap_user(name, password) do
|
||||
%User{} = user <- LDAP.bind_user(name, password) do
|
||||
{:ok, user}
|
||||
else
|
||||
{:ldap, _} ->
|
||||
|
@ -34,117 +30,4 @@ def get_user(%Plug.Conn{} = conn) do
|
|||
error
|
||||
end
|
||||
end
|
||||
|
||||
defp ldap_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, [])
|
||||
tlsopts = Keyword.get(ldap, :tlsopts, [])
|
||||
|
||||
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
|
||||
if Keyword.get(ldap, :tls, false) do
|
||||
:application.ensure_all_started(:ssl)
|
||||
|
||||
case :eldap.start_tls(
|
||||
connection,
|
||||
Keyword.merge(
|
||||
[
|
||||
verify: :verify_peer,
|
||||
cacerts: :certifi.cacerts(),
|
||||
customize_hostname_check: [
|
||||
fqdn_fun: fn _ -> to_charlist(host) end
|
||||
]
|
||||
],
|
||||
tlsopts
|
||||
),
|
||||
@connection_timeout
|
||||
) do
|
||||
:ok ->
|
||||
:ok
|
||||
|
||||
error ->
|
||||
Logger.error("Could not start TLS: #{inspect(error)}")
|
||||
end
|
||||
end
|
||||
|
||||
bind_user(connection, ldap, name, password)
|
||||
after
|
||||
:eldap.close(connection)
|
||||
end
|
||||
|
||||
{:error, error} ->
|
||||
Logger.error("Could not open LDAP connection: #{inspect(error)}")
|
||||
{:error, {:ldap_connection_error, error}}
|
||||
end
|
||||
end
|
||||
|
||||
defp bind_user(connection, ldap, name, password) do
|
||||
uid = Keyword.get(ldap, :uid, "cn")
|
||||
base = Keyword.get(ldap, :base)
|
||||
|
||||
case :eldap.simple_bind(connection, "#{uid}=#{name},#{base}", password) do
|
||||
:ok ->
|
||||
case fetch_user(name) do
|
||||
%User{} = user ->
|
||||
user
|
||||
|
||||
_ ->
|
||||
register_user(connection, base, uid, name)
|
||||
end
|
||||
|
||||
error ->
|
||||
Logger.error("Could not bind LDAP user #{name}: #{inspect(error)}")
|
||||
{:error, {:ldap_bind_error, error}}
|
||||
end
|
||||
end
|
||||
|
||||
defp register_user(connection, base, uid, name) 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
|
||||
# The :eldap_search_result record structure changed in OTP 24.3 and added a controls field
|
||||
# https://github.com/erlang/otp/pull/5538
|
||||
{:ok, {:eldap_search_result, [{:eldap_entry, _object, attributes}], _referrals}} ->
|
||||
try_register(name, attributes)
|
||||
|
||||
{:ok, {:eldap_search_result, [{:eldap_entry, _object, attributes}], _referrals, _controls}} ->
|
||||
try_register(name, attributes)
|
||||
|
||||
error ->
|
||||
Logger.error("Couldn't register user because LDAP search failed: #{inspect(error)}")
|
||||
{:error, {:ldap_search_error, error}}
|
||||
end
|
||||
end
|
||||
|
||||
defp try_register(name, attributes) do
|
||||
params = %{
|
||||
name: name,
|
||||
nickname: name,
|
||||
password: nil
|
||||
}
|
||||
|
||||
params =
|
||||
case List.keyfind(attributes, ~c"mail", 0) do
|
||||
{_, [mail]} -> Map.put_new(params, :email, :erlang.list_to_binary(mail))
|
||||
_ -> params
|
||||
end
|
||||
|
||||
changeset = User.register_changeset_ldap(%User{}, params)
|
||||
|
||||
case User.register(changeset) do
|
||||
{:ok, user} -> user
|
||||
error -> error
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
7
mix.exs
7
mix.exs
|
@ -9,7 +9,7 @@ def project do
|
|||
name: "pl",
|
||||
compat_name: "Pleroma",
|
||||
version: version("2.7.0"),
|
||||
elixir: "~> 1.13",
|
||||
elixir: "~> 1.14",
|
||||
elixirc_paths: elixirc_paths(Mix.env()),
|
||||
compilers: Mix.compilers(),
|
||||
elixirc_options: [warnings_as_errors: warnings_as_errors(), prune_code_paths: false],
|
||||
|
@ -156,7 +156,7 @@ defp deps do
|
|||
{:cachex, "~> 3.2"},
|
||||
{:csv, "~> 2.4"},
|
||||
{:tesla, "~> 1.11"},
|
||||
{:castore, "~> 0.1"},
|
||||
{:castore, "~> 1.0"},
|
||||
{:cowlib, "~> 2.9", override: true},
|
||||
{:gun, "~> 2.0.0-rc.1", override: true},
|
||||
{:finch, "~> 0.15"},
|
||||
|
@ -172,6 +172,8 @@ defp deps do
|
|||
{:swoosh, "~> 1.16.9"},
|
||||
{:phoenix_swoosh, "~> 1.1"},
|
||||
{:gen_smtp, "~> 0.13"},
|
||||
{:mua, "~> 0.2.0"},
|
||||
{:mail, "~> 0.3.0"},
|
||||
{:ex_syslogger, "~> 1.4"},
|
||||
{:floki, "~> 0.35"},
|
||||
{:timex, "~> 3.6"},
|
||||
|
@ -209,7 +211,6 @@ defp deps do
|
|||
{:icalendar, "~> 1.1"},
|
||||
{:geospatial, "~> 0.3.1"},
|
||||
{:argon2_elixir, "~> 4.0"},
|
||||
{:certifi, "~> 2.12"},
|
||||
|
||||
## dev & test
|
||||
{:phoenix_live_reload, "~> 1.3.3", only: :dev},
|
||||
|
|
6
mix.lock
6
mix.lock
|
@ -11,7 +11,7 @@
|
|||
"cachex": {:hex, :cachex, "3.6.0", "14a1bfbeee060dd9bec25a5b6f4e4691e3670ebda28c8ba2884b12fe30b36bf8", [:mix], [{:eternal, "~> 1.2", [hex: :eternal, repo: "hexpm", optional: false]}, {:jumper, "~> 1.0", [hex: :jumper, repo: "hexpm", optional: false]}, {:sleeplocks, "~> 1.1", [hex: :sleeplocks, repo: "hexpm", optional: false]}, {:unsafe, "~> 1.0", [hex: :unsafe, repo: "hexpm", optional: false]}], "hexpm", "ebf24e373883bc8e0c8d894a63bbe102ae13d918f790121f5cfe6e485cc8e2e2"},
|
||||
"calendar": {:hex, :calendar, "1.0.0", "f52073a708528482ec33d0a171954ca610fe2bd28f1e871f247dc7f1565fa807", [:mix], [{:tzdata, "~> 0.5.20 or ~> 0.1.201603 or ~> 1.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "990e9581920c82912a5ee50e62ff5ef96da6b15949a2ee4734f935fdef0f0a6f"},
|
||||
"captcha": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/elixir-captcha.git", "6630c42aaaab124e697b4e513190c89d8b64e410", [ref: "6630c42aaaab124e697b4e513190c89d8b64e410"]},
|
||||
"castore": {:hex, :castore, "0.1.22", "4127549e411bedd012ca3a308dede574f43819fe9394254ca55ab4895abfa1a2", [:mix], [], "hexpm", "c17576df47eb5aa1ee40cc4134316a99f5cad3e215d5c77b8dd3cfef12a22cac"},
|
||||
"castore": {:hex, :castore, "1.0.8", "dedcf20ea746694647f883590b82d9e96014057aff1d44d03ec90f36a5c0dc6e", [:mix], [], "hexpm", "0b2b66d2ee742cb1d9cb8c8be3b43c3a70ee8651f37b75a8b982e036752983f1"},
|
||||
"cc_precompiler": {:hex, :cc_precompiler, "0.1.9", "e8d3364f310da6ce6463c3dd20cf90ae7bbecbf6c5203b98bf9b48035592649b", [:mix], [{:elixir_make, "~> 0.7", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "9dcab3d0f3038621f1601f13539e7a9ee99843862e66ad62827b0c42b2f58a54"},
|
||||
"certifi": {:hex, :certifi, "2.12.0", "2d1cca2ec95f59643862af91f001478c9863c2ac9cb6e2f89780bfd8de987329", [:rebar3], [], "hexpm", "ee68d85df22e554040cdb4be100f33873ac6051387baf6a8f6ce82272340ff1c"},
|
||||
"combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"},
|
||||
|
@ -80,6 +80,7 @@
|
|||
"jumper": {:hex, :jumper, "1.0.2", "68cdcd84472a00ac596b4e6459a41b3062d4427cbd4f1e8c8793c5b54f1406a7", [:mix], [], "hexpm", "9b7782409021e01ab3c08270e26f36eb62976a38c1aa64b2eaf6348422f165e1"},
|
||||
"linkify": {:hex, :linkify, "0.5.3", "5f8143d8f61f5ff08d3aeeff47ef6509492b4948d8f08007fbf66e4d2246a7f2", [:mix], [], "hexpm", "3ef35a1377d47c25506e07c1c005ea9d38d700699d92ee92825f024434258177"},
|
||||
"logger_backends": {:hex, :logger_backends, "1.0.0", "09c4fad6202e08cb0fbd37f328282f16539aca380f512523ce9472b28edc6bdf", [:mix], [], "hexpm", "1faceb3e7ec3ef66a8f5746c5afd020e63996df6fd4eb8cdb789e5665ae6c9ce"},
|
||||
"mail": {:hex, :mail, "0.3.1", "cb0a14e4ed8904e4e5a08214e686ccf6f9099346885db17d8c309381f865cc5c", [:mix], [], "hexpm", "1db701e89865c1d5fa296b2b57b1cd587587cca8d8a1a22892b35ef5a8e352a6"},
|
||||
"majic": {:hex, :majic, "1.0.0", "37e50648db5f5c2ff0c9fb46454d034d11596c03683807b9fb3850676ffdaab3", [:make, :mix], [{:elixir_make, "~> 0.6.1", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "7905858f76650d49695f14ea55cd9aaaee0c6654fa391671d4cf305c275a0a9e"},
|
||||
"makeup": {:hex, :makeup, "1.0.5", "d5a830bc42c9800ce07dd97fa94669dfb93d3bf5fcf6ea7a0c67b2e0e4a7f26c", [:mix], [{:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cfa158c02d3f5c0c665d0af11512fed3fba0144cf1aadee0f2ce17747fba2ca9"},
|
||||
"makeup_elixir": {:hex, :makeup_elixir, "0.14.1", "4f0e96847c63c17841d42c08107405a005a2680eb9c7ccadfd757bd31dabccfb", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "f2438b1a80eaec9ede832b5c41cd4f373b38fd7aa33e3b22d9db79e640cbde11"},
|
||||
|
@ -93,6 +94,7 @@
|
|||
"mock": {:hex, :mock, "0.3.8", "7046a306b71db2488ef54395eeb74df0a7f335a7caca4a3d3875d1fc81c884dd", [:mix], [{:meck, "~> 0.9.2", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm", "7fa82364c97617d79bb7d15571193fc0c4fe5afd0c932cef09426b3ee6fe2022"},
|
||||
"mogrify": {:hex, :mogrify, "0.9.3", "238c782f00271dace01369ad35ae2e9dd020feee3443b9299ea5ea6bed559841", [:mix], [], "hexpm", "0189b1e1de27455f2b9ae8cf88239cefd23d38de9276eb5add7159aea51731e6"},
|
||||
"mox": {:hex, :mox, "1.1.0", "0f5e399649ce9ab7602f72e718305c0f9cdc351190f72844599545e4996af73c", [:mix], [], "hexpm", "d44474c50be02d5b72131070281a5d3895c0e7a95c780e90bc0cfe712f633a13"},
|
||||
"mua": {:hex, :mua, "0.2.3", "46b29b7b2bb14105c0b7be9526f7c452df17a7841b30b69871c024a822ff551c", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}], "hexpm", "7fe861a87fcc06a980d3941bbcb2634e5f0f30fd6ad15ef6c0423ff9dc7e46de"},
|
||||
"multipart": {:hex, :multipart, "0.4.0", "634880a2148d4555d050963373d0e3bbb44a55b2badd87fa8623166172e9cda0", [:mix], [{:mime, "~> 1.2 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}], "hexpm", "3c5604bc2fb17b3137e5d2abdf5dacc2647e60c5cc6634b102cf1aef75a06f0a"},
|
||||
"nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"},
|
||||
"nimble_parsec": {:hex, :nimble_parsec, "0.6.0", "32111b3bf39137144abd7ba1cce0914533b2d16ef35e8abc5ec8be6122944263", [:mix], [], "hexpm", "27eac315a94909d4dc68bc07a4a83e06c8379237c5ea528a9acff4ca1c873c52"},
|
||||
|
@ -100,7 +102,7 @@
|
|||
"nodex": {:git, "https://git.pleroma.social/pleroma/nodex", "cb6730f943cfc6aad674c92161be23a8411f15d1", [ref: "cb6730f943cfc6aad674c92161be23a8411f15d1"]},
|
||||
"oauth2": {:hex, :oauth2, "0.9.4", "632e8e8826a45e33ac2ea5ac66dcc019ba6bb5a0d2ba77e342d33e3b7b252c6e", [:mix], [{:hackney, "~> 1.7", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "407c6b9f60aa0d01b915e2347dc6be78adca706a37f0c530808942da3b62e7af"},
|
||||
"oauther": {:hex, :oauther, "1.3.0", "82b399607f0ca9d01c640438b34d74ebd9e4acd716508f868e864537ecdb1f76", [:mix], [], "hexpm", "78eb888ea875c72ca27b0864a6f550bc6ee84f2eeca37b093d3d833fbcaec04e"},
|
||||
"oban": {:hex, :oban, "2.18.2", "583e78965ee15263ac968e38c983bad169ae55eadaa8e1e39912562badff93ba", [:mix], [{:ecto_sql, "~> 3.10", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:ecto_sqlite3, "~> 0.9", [hex: :ecto_sqlite3, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "9dd25fd35883a91ed995e9fe516e479344d3a8623dfe2b8c3fc8e5be0228ec3a"},
|
||||
"oban": {:hex, :oban, "2.18.3", "1608c04f8856c108555c379f2f56bc0759149d35fa9d3b825cb8a6769f8ae926", [:mix], [{:ecto_sql, "~> 3.10", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:ecto_sqlite3, "~> 0.9", [hex: :ecto_sqlite3, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "36ca6ca84ef6518f9c2c759ea88efd438a3c81d667ba23b02b062a0aa785475e"},
|
||||
"oban_live_dashboard": {:hex, :oban_live_dashboard, "0.1.1", "8aa4ceaf381c818f7d5c8185cc59942b8ac82ef0cf559881aacf8d3f8ac7bdd3", [:mix], [{:oban, "~> 2.15", [hex: :oban, repo: "hexpm", optional: false]}, {:phoenix_live_dashboard, "~> 0.7", [hex: :phoenix_live_dashboard, repo: "hexpm", optional: false]}], "hexpm", "16dc4ce9c9a95aa2e655e35ed4e675652994a8def61731a18af85e230e1caa63"},
|
||||
"octo_fetch": {:hex, :octo_fetch, "0.4.0", "074b5ecbc08be10b05b27e9db08bc20a3060142769436242702931c418695b19", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm", "cf8be6f40cd519d7000bb4e84adcf661c32e59369ca2827c4e20042eda7a7fc6"},
|
||||
"open_api_spex": {:hex, :open_api_spex, "3.18.2", "8c855e83bfe8bf81603d919d6e892541eafece3720f34d1700b58024dadde247", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, "~> 3.0 or ~> 4.0 or ~> 5.0", [hex: :poison, repo: "hexpm", optional: true]}, {:ymlr, "~> 2.0 or ~> 3.0 or ~> 4.0", [hex: :ymlr, repo: "hexpm", optional: true]}], "hexpm", "aa3e6dcfc0ad6a02596b2172662da21c9dd848dac145ea9e603f54e3d81b8d2b"},
|
||||
|
|
|
@ -28,11 +28,7 @@ test "authorizes the existing user using LDAP credentials" do
|
|||
{:eldap, [],
|
||||
[
|
||||
open: fn [^host], [{:port, ^port}, {:ssl, false} | _] -> {:ok, self()} end,
|
||||
simple_bind: fn _connection, _dn, ^password -> :ok end,
|
||||
close: fn _connection ->
|
||||
send(self(), :close_connection)
|
||||
:ok
|
||||
end
|
||||
simple_bind: fn _connection, _dn, ^password -> :ok end
|
||||
]}
|
||||
] do
|
||||
conn =
|
||||
|
@ -50,7 +46,6 @@ test "authorizes the existing user using LDAP credentials" do
|
|||
token = Repo.get_by(Token, token: token)
|
||||
|
||||
assert token.user_id == user.id
|
||||
assert_received :close_connection
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -72,10 +67,6 @@ test "creates a new user after successful LDAP authorization" do
|
|||
wholeSubtree: fn -> :ok end,
|
||||
search: fn _connection, _options ->
|
||||
{:ok, {:eldap_search_result, [{:eldap_entry, ~c"", []}], []}}
|
||||
end,
|
||||
close: fn _connection ->
|
||||
send(self(), :close_connection)
|
||||
:ok
|
||||
end
|
||||
]}
|
||||
] do
|
||||
|
@ -94,7 +85,6 @@ test "creates a new user after successful LDAP authorization" do
|
|||
token = Repo.get_by(Token, token: token) |> Repo.preload(:user)
|
||||
|
||||
assert token.user.nickname == user.nickname
|
||||
assert_received :close_connection
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -111,11 +101,7 @@ test "disallow authorization for wrong LDAP credentials" do
|
|||
{:eldap, [],
|
||||
[
|
||||
open: fn [^host], [{:port, ^port}, {:ssl, false} | _] -> {:ok, self()} end,
|
||||
simple_bind: fn _connection, _dn, ^password -> {:error, :invalidCredentials} end,
|
||||
close: fn _connection ->
|
||||
send(self(), :close_connection)
|
||||
:ok
|
||||
end
|
||||
simple_bind: fn _connection, _dn, ^password -> {:error, :invalidCredentials} end
|
||||
]}
|
||||
] do
|
||||
conn =
|
||||
|
@ -129,7 +115,6 @@ test "disallow authorization for wrong LDAP credentials" do
|
|||
})
|
||||
|
||||
assert %{"error" => "Invalid credentials"} = json_response(conn, 400)
|
||||
assert_received :close_connection
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue