diff --git a/.woodpecker.yml b/.woodpecker.yml
new file mode 100644
index 000000000..be497b531
--- /dev/null
+++ b/.woodpecker.yml
@@ -0,0 +1,187 @@
+variables:
+ - &scw-secrets
+ - SCW_ACCESS_KEY
+ - SCW_SECRET_KEY
+ - SCW_DEFAULT_ORGANIZATION_ID
+ - &setup-hex "mix local.hex --force && mix local.rebar --force"
+ - &on-release
+ when:
+ event:
+ - push
+ - tag
+ branch:
+ - develop
+ - stable
+ - refs/tags/v*
+ - refs/tags/stable-*
+ - &on-point-release
+ when:
+ event:
+ - push
+ branch:
+ - develop
+ - stable
+ - &on-pr-open
+ when:
+ event:
+ - pull_request
+
+ - &tag-build "export BUILD_TAG=$${CI_COMMIT_TAG:-\"$CI_COMMIT_BRANCH\"} && export PLEROMA_BUILD_BRANCH=$BUILD_TAG"
+
+ - &clean "(rm -rf release || true) && (rm -rf _build || true) && (rm -rf /root/.mix)"
+
+services:
+ postgres:
+ image: postgres:13
+ when:
+ event:
+ - pull_request
+ environment:
+ POSTGRES_DB: pleroma_test
+ POSTGRES_USER: postgres
+ POSTGRES_PASSWORD: postgres
+
+pipeline:
+ lint:
+ <<: *on-pr-open
+ image: akkoma/ci-base:latest
+ commands:
+ - mix local.hex --force
+ - mix local.rebar --force
+ - mix format --check-formatted
+
+ build:
+ image: akkoma/ci-base:latest
+ <<: *on-pr-open
+ environment:
+ MIX_ENV: test
+ POSTGRES_DB: pleroma_test
+ POSTGRES_USER: postgres
+ POSTGRES_PASSWORD: postgres
+ DB_HOST: postgres
+ commands:
+ - mix local.hex --force
+ - mix local.rebar --force
+ - mix deps.get
+ - mix compile
+
+ test:
+ image: akkoma/ci-base:latest
+ <<: *on-pr-open
+ environment:
+ MIX_ENV: test
+ POSTGRES_DB: pleroma_test
+ POSTGRES_USER: postgres
+ POSTGRES_PASSWORD: postgres
+ DB_HOST: postgres
+ commands:
+ - mix local.hex --force
+ - mix local.rebar --force
+ - mix deps.get
+ - mix compile
+ - mix ecto.drop -f -q
+ - mix ecto.create
+ - mix ecto.migrate
+ - mix test --preload-modules --exclude erratic --exclude federated --max-cases 4
+
+ # Canonical amd64
+ ubuntu22:
+ image: hexpm/elixir:1.13.4-erlang-25.0.2-ubuntu-jammy-20220428
+ <<: *on-release
+ environment:
+ MIX_ENV: prod
+ DEBIAN_FRONTEND: noninteractive
+ commands:
+ - rm config/emoji.txt
+ - apt-get update && apt-get install -y cmake libmagic-dev rclone zip imagemagick libmagic-dev git build-essential g++ wget
+ - *clean
+ - echo "import Config" > config/prod.secret.exs
+ - *setup-hex
+ - *tag-build
+ - mix deps.get --only prod
+ - mix release --path release
+ - zip akkoma-amd64.zip -r release
+
+ release-ubuntu22:
+ image: akkoma/releaser
+ <<: *on-release
+ secrets: *scw-secrets
+ commands:
+ - export SOURCE=akkoma-amd64.zip
+ - export DEST=scaleway:akkoma-updates/$${CI_COMMIT_TAG:-"$CI_COMMIT_BRANCH"}/akkoma-amd64.zip
+ - /bin/sh /entrypoint.sh
+ environment:
+ SOURCE: akkoma-amd64.zip
+ DEST: scaleway:akkoma-updates/$${CI_COMMIT_TAG:-"$CI_COMMIT_BRANCH"}/akkoma-amd64.zip
+
+ debian-bullseye:
+ image: elixir:1.13.4
+ <<: *on-release
+ environment:
+ MIX_ENV: prod
+ DEBIAN_FRONTEND: noninteractive
+ commands:
+ - apt-get update && apt-get install -y cmake libmagic-dev rclone zip imagemagick libmagic-dev git build-essential gcc make g++ wget
+ - *clean
+ - echo "import Config" > config/prod.secret.exs
+ - *setup-hex
+ - *tag-build
+ - mix deps.get --only prod
+ - mix release --path release
+ - zip akkoma-amd64.zip -r release
+
+ release-debian:
+ image: akkoma/releaser
+ <<: *on-release
+ secrets: *scw-secrets
+ commands:
+ - export SOURCE=akkoma-amd64.zip
+ - export DEST=scaleway:akkoma-updates/$${CI_COMMIT_TAG:-"$CI_COMMIT_BRANCH"}/akkoma-debian-bullseye.zip
+ - /bin/sh /entrypoint.sh
+
+ # Canonical amd64-musl
+ musl:
+ image: elixir:1.13.4-alpine
+ <<: *on-release
+ environment:
+ MIX_ENV: prod
+ commands:
+ - apk add git gcc g++ musl-dev make cmake file-dev rclone wget zip imagemagick
+ - *clean
+ - *setup-hex
+ - mix deps.clean --all
+ - *tag-build
+ - mix deps.get --only prod
+ - mix release --path release
+ - zip akkoma-amd64-musl.zip -r release
+
+ release-musl:
+ image: akkoma/releaser
+ <<: *on-release
+ secrets: *scw-secrets
+ commands:
+ - export SOURCE=akkoma-amd64-musl.zip
+ - export DEST=scaleway:akkoma-updates/$${CI_COMMIT_TAG:-"$CI_COMMIT_BRANCH"}/akkoma-amd64-musl.zip
+ - /bin/sh /entrypoint.sh
+
+ docs:
+ <<: *on-point-release
+ secrets:
+ - SCW_ACCESS_KEY
+ - SCW_SECRET_KEY
+ - SCW_DEFAULT_ORGANIZATION_ID
+ environment:
+ CI: "true"
+ image: python:3.10-slim
+ commands:
+ - apt-get update && apt-get install -y rclone wget git zip
+ - wget https://github.com/scaleway/scaleway-cli/releases/download/v2.5.1/scaleway-cli_2.5.1_linux_amd64
+ - mv scaleway-cli_2.5.1_linux_amd64 scaleway-cli
+ - chmod +x scaleway-cli
+ - ./scaleway-cli object config install type=rclone
+ - cd docs
+ - pip install -r requirements.txt
+ - mkdocs build
+ - zip -r docs.zip site/*
+ - cd site
+ - rclone copy . scaleway:akkoma-docs/$CI_COMMIT_BRANCH/
diff --git a/.woodpecker/.docs.yml b/.woodpecker/.docs.yml
deleted file mode 100644
index 61b369e51..000000000
--- a/.woodpecker/.docs.yml
+++ /dev/null
@@ -1,27 +0,0 @@
-pipeline:
- build:
- when:
- event:
- - push
- branch:
- - develop
- - stable
- secrets:
- - SCW_ACCESS_KEY
- - SCW_SECRET_KEY
- - SCW_DEFAULT_ORGANIZATION_ID
- environment:
- CI: "true"
- image: python:3.10-slim
- commands:
- - apt-get update && apt-get install -y rclone wget git zip
- - wget https://github.com/scaleway/scaleway-cli/releases/download/v2.5.1/scaleway-cli_2.5.1_linux_amd64
- - mv scaleway-cli_2.5.1_linux_amd64 scaleway-cli
- - chmod +x scaleway-cli
- - ./scaleway-cli object config install type=rclone
- - cd docs
- - pip install -r requirements.txt
- - mkdocs build
- - zip -r docs.zip site/*
- - cd site
- - rclone copy . scaleway:akkoma-docs/$CI_COMMIT_BRANCH/
diff --git a/.woodpecker/.release.yml b/.woodpecker/.release.yml
deleted file mode 100644
index 535cdd65b..000000000
--- a/.woodpecker/.release.yml
+++ /dev/null
@@ -1,59 +0,0 @@
-variables:
- - &scw-secrets
- - SCW_ACCESS_KEY
- - SCW_SECRET_KEY
- - SCW_DEFAULT_ORGANIZATION_ID
- - &setup-scw-s3 "wget https://github.com/scaleway/scaleway-cli/releases/download/v2.5.1/scaleway-cli_2.5.1_linux_amd64 && mv scaleway-cli_2.5.1_linux_amd64 scaleway-cli && chmod +x scaleway-cli && ./scaleway-cli object config install type=rclone"
-
- - &setup-hex "mix local.hex --force && mix local.rebar --force"
- - &build-on
- when:
- event:
- - push
- - tag
- branch:
- - develop
- - stable
- - refs/tags/v*
- - refs/tags/stable-*
- - &tag-build 'export BUILD_TAG=$${CI_COMMIT_TAG:-"$CI_COMMIT_BRANCH"} && export PLEROMA_BUILD_BRANCH=$BUILD_TAG'
-
- - &clean "(rm -rf release || true) && (rm -rf _build || true) && (rm -rf /root/.mix) && (rm scaleway-cli || true)"
-
-
-pipeline:
- glibc:
- image: elixir:1.13
- <<: *build-on
- secrets: *scw-secrets
- environment:
- MIX_ENV: prod
- commands:
- - apt-get update && apt-get install -y cmake libmagic-dev rclone zip imagemagick libmagic-dev git
- - *clean
- - *setup-scw-s3
- - echo "import Mix.Config" > config/prod.secret.exs
- - *setup-hex
- - *tag-build
- - mix deps.get --only prod
- - mix release --path release
- - zip akkoma-amd64.zip -r release
- - rclone copyto akkoma-amd64.zip scaleway:akkoma-updates/$BUILD_TAG/akkoma-amd64.zip
-
- musl:
- image: elixir:1.13-alpine
- <<: *build-on
- secrets: *scw-secrets
- environment:
- MIX_ENV: prod
- commands:
- - apk add git gcc g++ musl-dev make cmake file-dev rclone wget zip imagemagick
- - *clean
- - *setup-scw-s3
- - *setup-hex
- - mix deps.clean --all
- - *tag-build
- - mix deps.get --only prod
- - mix release --path release
- - zip akkoma-amd64.zip -r release
- - rclone copyto akkoma-amd64.zip scaleway:akkoma-updates/$BUILD_TAG/akkoma-amd64-musl.zip
diff --git a/.woodpecker/.test.yml b/.woodpecker/.test.yml
deleted file mode 100644
index f41655029..000000000
--- a/.woodpecker/.test.yml
+++ /dev/null
@@ -1,59 +0,0 @@
-matrix:
- ELIXIR_VERSION:
- - 1.13
-
-pipeline:
- lint:
- when:
- event:
- - pull_request
- image: pleromaforkci/ci-base:1.13
- commands:
- - mix local.hex --force
- - mix local.rebar --force
- - mix format --check-formatted
-
- build:
- image: pleromaforkci/ci-base:${ELIXIR_VERSION}
- when:
- event:
- - pull_request
- environment:
- MIX_ENV: test
- commands:
- - mix local.hex --force
- - mix local.rebar --force
- - mix deps.get
- - mix compile
-
- test:
- group: test
- image: pleromaforkci/ci-base:${ELIXIR_VERSION}
- when:
- event:
- - pull_request
- environment:
- MIX_ENV: test
- POSTGRES_DB: pleroma_test
- POSTGRES_USER: postgres
- POSTGRES_PASSWORD: postgres
- DB_HOST: postgres
- commands:
- - mix local.hex --force
- - mix local.rebar --force
- - mix deps.get
- - mix ecto.drop -f -q
- - mix ecto.create
- - mix ecto.migrate
- - mix test --preload-modules --exclude erratic --exclude federated --max-cases 4
-
-services:
- postgres:
- image: postgres:13
- when:
- event:
- - pull_request
- environment:
- POSTGRES_DB: pleroma_test
- POSTGRES_USER: postgres
- POSTGRES_PASSWORD: postgres
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b75720f8d..98f434aaa 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -26,7 +26,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- `/api/v1/notifications/dismiss`
- `/api/v1/search`
- `/api/v1/statuses/{id}/card`
-- LDAP authenticator (use the akkoma-contrib-authenticator-ldap runtime module)
- Chats, they were half-baked. Just use PMs.
- Prometheus, it causes massive slowdown
diff --git a/Dockerfile b/Dockerfile
index e6210affb..42ba9616b 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,4 +1,4 @@
-FROM hexpm/elixir:1.13.4-erlang-24.3.4.2-alpine-3.16.0 as build
+FROM elixir:1.13.4-alpine as build
COPY . .
diff --git a/config/config.exs b/config/config.exs
index bac167c29..197887c93 100644
--- a/config/config.exs
+++ b/config/config.exs
@@ -509,7 +509,6 @@ config :pleroma, Pleroma.User,
"~",
"about",
"activities",
- "akkoma",
"api",
"auth",
"check_password",
@@ -590,6 +589,17 @@ config :pleroma, Pleroma.Formatter,
extra: true,
validate_tld: :no_scheme
+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: [],
+ 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"
+
oauth_consumer_strategies =
"OAUTH_CONSUMER_STRATEGIES"
|> System.get_env()
diff --git a/config/description.exs b/config/description.exs
index b8a053c3c..e864f090c 100644
--- a/config/description.exs
+++ b/config/description.exs
@@ -2140,6 +2140,104 @@ config :pleroma, :config_description, [
}
]
},
+ %{
+ group: :pleroma,
+ key: :ldap,
+ label: "LDAP",
+ type: :group,
+ description:
+ "Use LDAP for user authentication. When a user logs in to the Pleroma instance, the name and password" <>
+ " will be verified by trying to authenticate (bind) to a LDAP server." <>
+ " If a user exists in the LDAP directory but there is no account with the same name yet on the" <>
+ " Pleroma instance then a new Pleroma account will be created with the same name as the LDAP user name.",
+ children: [
+ %{
+ key: :enabled,
+ type: :boolean,
+ description: "Enables LDAP authentication"
+ },
+ %{
+ key: :host,
+ type: :string,
+ description: "LDAP server hostname",
+ suggestions: ["localhosts"]
+ },
+ %{
+ key: :port,
+ type: :integer,
+ description: "LDAP port, e.g. 389 or 636",
+ suggestions: [389, 636]
+ },
+ %{
+ key: :ssl,
+ label: "SSL",
+ type: :boolean,
+ description: "Enable to use SSL, usually implies the port 636"
+ },
+ %{
+ key: :sslopts,
+ label: "SSL options",
+ type: :keyword,
+ description: "Additional SSL options",
+ suggestions: [cacertfile: "path/to/file/with/PEM/cacerts", 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,
+ description: "Type of cert verification",
+ suggestions: [:verify_peer]
+ }
+ ]
+ },
+ %{
+ key: :tls,
+ label: "TLS",
+ type: :boolean,
+ description: "Enable to use STARTTLS, usually implies the port 389"
+ },
+ %{
+ key: :tlsopts,
+ label: "TLS options",
+ type: :keyword,
+ description: "Additional TLS options",
+ suggestions: [cacertfile: "path/to/file/with/PEM/cacerts", 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,
+ description: "Type of cert verification",
+ suggestions: [:verify_peer]
+ }
+ ]
+ },
+ %{
+ key: :base,
+ type: :string,
+ description: "LDAP base, e.g. \"dc=example,dc=com\"",
+ suggestions: ["dc=example,dc=com"]
+ },
+ %{
+ key: :uid,
+ label: "UID",
+ type: :string,
+ description:
+ "LDAP attribute name to authenticate the user, e.g. when \"cn\", the filter will be \"cn=username,base\"",
+ suggestions: ["cn"]
+ }
+ ]
+ },
%{
group: :pleroma,
key: :auth,
diff --git a/docs/docs/configuration/cheatsheet.md b/docs/docs/configuration/cheatsheet.md
index fdc39c0de..bac20070f 100644
--- a/docs/docs/configuration/cheatsheet.md
+++ b/docs/docs/configuration/cheatsheet.md
@@ -891,6 +891,28 @@ Authentication / authorization settings.
### Pleroma.Web.Auth.Authenticator
* `Pleroma.Web.Auth.PleromaAuthenticator`: default database authenticator.
+* `Pleroma.Web.Auth.LDAPAuthenticator`: LDAP authentication.
+
+### :ldap
+
+Use LDAP for user authentication. When a user logs in to the Akkoma
+instance, the name and password will be verified by trying to authenticate
+(bind) to an LDAP server. If a user exists in the LDAP directory but there
+is no account with the same name yet on the Akkoma instance then a new
+Akkoma 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
+* `sslopts`: additional SSL options
+* `tls`: true to start TLS, usually implies the 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"
+
+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"`.
### :oauth2 (Akkoma as OAuth 2.0 provider settings)
diff --git a/docs/docs/installation/alpine_linux_en.md b/docs/docs/installation/alpine_linux_en.md
index 3be69af6e..f98998fb8 100644
--- a/docs/docs/installation/alpine_linux_en.md
+++ b/docs/docs/installation/alpine_linux_en.md
@@ -41,6 +41,12 @@ doas apk add git build-base cmake file-dev
doas apk add erlang elixir
```
+* Install `erlang-eldap` if you want to enable ldap authenticator
+
+```shell
+doas apk add erlang-eldap
+```
+
### Install PostgreSQL
* Install Postgresql server:
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
index 275ad9506..2a1b5af94 100644
--- a/lib/pleroma/user.ex
+++ b/lib/pleroma/user.ex
@@ -660,6 +660,31 @@ defmodule Pleroma.User do
@spec force_password_reset(User.t()) :: {:ok, User.t()} | {:error, Ecto.Changeset.t()}
def force_password_reset(user), do: update_password_reset_pending(user, true)
+ # Used to auto-register LDAP accounts which won't have a password hash stored locally
+ def register_changeset_ldap(struct, params = %{password: password})
+ when is_nil(password) do
+ params =
+ if Map.has_key?(params, :email) do
+ Map.put_new(params, :email, params[:email])
+ else
+ params
+ end
+
+ struct
+ |> cast(params, [
+ :name,
+ :nickname,
+ :email
+ ])
+ |> validate_required([:name, :nickname])
+ |> unique_constraint(:nickname)
+ |> validate_exclusion(:nickname, Config.get([User, :restricted_nicknames]))
+ |> validate_format(:nickname, local_nickname_regex())
+ |> put_ap_id()
+ |> unique_constraint(:ap_id)
+ |> put_following_and_follower_and_featured_address()
+ end
+
def register_changeset(struct, params \\ %{}, opts \\ []) do
bio_limit = Config.get([:instance, :user_bio_length], 5000)
name_limit = Config.get([:instance, :user_name_length], 100)
diff --git a/lib/pleroma/web/auth/ldap_authenticator.ex b/lib/pleroma/web/auth/ldap_authenticator.ex
new file mode 100644
index 000000000..f77e8d203
--- /dev/null
+++ b/lib/pleroma/web/auth/ldap_authenticator.ex
@@ -0,0 +1,129 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2021 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.Auth.LDAPAuthenticator do
+ alias Pleroma.User
+
+ require Logger
+
+ import Pleroma.Web.Auth.Helpers, only: [fetch_credentials: 1, fetch_user: 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
+ defdelegate auth_template, to: @base
+ defdelegate oauth_consumer_template, to: @base
+
+ 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
+ {:ok, user}
+ else
+ {:ldap, _} ->
+ @base.get_user(conn)
+
+ error ->
+ 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, [])
+
+ 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.get(ldap, :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 ->
+ 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
+ {:ok, {:eldap_search_result, [{:eldap_entry, _, attributes}], _}} ->
+ params = %{
+ name: name,
+ nickname: name,
+ password: nil
+ }
+
+ params =
+ case List.keyfind(attributes, '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
+
+ error ->
+ error
+ end
+ end
+end
diff --git a/mix.exs b/mix.exs
index ff54c79b4..71384c755 100644
--- a/mix.exs
+++ b/mix.exs
@@ -9,6 +9,7 @@ defmodule Pleroma.Mixfile do
elixirc_paths: elixirc_paths(Mix.env()),
compilers: [:phoenix, :gettext] ++ Mix.compilers(),
elixirc_options: [warnings_as_errors: warnings_as_errors()],
+ xref: [exclude: [:eldap]],
start_permanent: Mix.env() == :prod,
aliases: aliases(),
deps: deps(),
diff --git a/rel/files/bin/pleroma_ctl b/rel/files/bin/pleroma_ctl
index 5aebda2fb..176287fcb 100755
--- a/rel/files/bin/pleroma_ctl
+++ b/rel/files/bin/pleroma_ctl
@@ -2,6 +2,7 @@
# XXX: This should be removed when elixir's releases get custom command support
detect_flavour() {
+ echo "Trying to autodetect flavour, you may want to override this with --flavour"
arch="$(uname -m)"
if [ "$arch" = "x86_64" ]; then
arch="amd64"
@@ -101,6 +102,7 @@ update() {
echo "Restoring erlang cookie"
echo $erlang_cookie > $RELEASE_ROOT/releases/COOKIE
echo "Done! Please refer to the changelog/release notes for changes and update instructions"
+ echo "You probably also want to update your frontend!"
set +e
}
diff --git a/test/pleroma/web/o_auth/ldap_authorization_test.exs b/test/pleroma/web/o_auth/ldap_authorization_test.exs
new file mode 100644
index 000000000..61b9ce6b7
--- /dev/null
+++ b/test/pleroma/web/o_auth/ldap_authorization_test.exs
@@ -0,0 +1,135 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2021 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.OAuth.LDAPAuthorizationTest do
+ use Pleroma.Web.ConnCase
+ alias Pleroma.Repo
+ alias Pleroma.Web.OAuth.Token
+ import Pleroma.Factory
+ import Mock
+
+ @skip if !Code.ensure_loaded?(:eldap), do: :skip
+
+ setup_all do: clear_config([:ldap, :enabled], true)
+
+ setup_all do: clear_config(Pleroma.Web.Auth.Authenticator, Pleroma.Web.Auth.LDAPAuthenticator)
+
+ @tag @skip
+ test "authorizes the existing user using LDAP credentials" do
+ password = "testpassword"
+ user = insert(:user, password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt(password))
+ app = insert(:oauth_app, scopes: ["read", "write"])
+
+ host = Pleroma.Config.get([:ldap, :host]) |> to_charlist
+ port = Pleroma.Config.get([:ldap, :port])
+
+ with_mocks [
+ {: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
+ ]}
+ ] do
+ conn =
+ build_conn()
+ |> post("/oauth/token", %{
+ "grant_type" => "password",
+ "username" => user.nickname,
+ "password" => password,
+ "client_id" => app.client_id,
+ "client_secret" => app.client_secret
+ })
+
+ assert %{"access_token" => token} = json_response(conn, 200)
+
+ token = Repo.get_by(Token, token: token)
+
+ assert token.user_id == user.id
+ assert_received :close_connection
+ end
+ end
+
+ @tag @skip
+ test "creates a new user after successful LDAP authorization" do
+ password = "testpassword"
+ user = build(:user)
+ app = insert(:oauth_app, scopes: ["read", "write"])
+
+ host = Pleroma.Config.get([:ldap, :host]) |> to_charlist
+ port = Pleroma.Config.get([:ldap, :port])
+
+ with_mocks [
+ {:eldap, [],
+ [
+ open: fn [^host], [{:port, ^port}, {:ssl, false} | _] -> {:ok, self()} end,
+ simple_bind: fn _connection, _dn, ^password -> :ok end,
+ equalityMatch: fn _type, _value -> :ok end,
+ wholeSubtree: fn -> :ok end,
+ search: fn _connection, _options ->
+ {:ok, {:eldap_search_result, [{:eldap_entry, '', []}], []}}
+ end,
+ close: fn _connection ->
+ send(self(), :close_connection)
+ :ok
+ end
+ ]}
+ ] do
+ conn =
+ build_conn()
+ |> post("/oauth/token", %{
+ "grant_type" => "password",
+ "username" => user.nickname,
+ "password" => password,
+ "client_id" => app.client_id,
+ "client_secret" => app.client_secret
+ })
+
+ assert %{"access_token" => token} = json_response(conn, 200)
+
+ token = Repo.get_by(Token, token: token) |> Repo.preload(:user)
+
+ assert token.user.nickname == user.nickname
+ assert_received :close_connection
+ end
+ end
+
+ @tag @skip
+ test "disallow authorization for wrong LDAP credentials" do
+ password = "testpassword"
+ user = insert(:user, password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt(password))
+ app = insert(:oauth_app, scopes: ["read", "write"])
+
+ host = Pleroma.Config.get([:ldap, :host]) |> to_charlist
+ port = Pleroma.Config.get([:ldap, :port])
+
+ with_mocks [
+ {: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
+ ]}
+ ] do
+ conn =
+ build_conn()
+ |> post("/oauth/token", %{
+ "grant_type" => "password",
+ "username" => user.nickname,
+ "password" => password,
+ "client_id" => app.client_id,
+ "client_secret" => app.client_secret
+ })
+
+ assert %{"error" => "Invalid credentials"} = json_response(conn, 400)
+ assert_received :close_connection
+ end
+ end
+end