From 93b3f41c7687a1418c3f11190f00caef17d92c12 Mon Sep 17 00:00:00 2001 From: FloatingGhost Date: Wed, 27 Jul 2022 10:40:45 +0100 Subject: [PATCH 1/4] prepare for ubuntu upgrade use commit ref commit sha select platforms use official docker image make sure we specify build ref test full build make sure we have g++ make sure we have wget make sure apt doesn't hang the build fix deps skip ubuntu20 for now skip it tag the fedora build fedora deps make sure env is set ensure locale remove --verbose flag see what happens if i remove hex what install mix why though? combine one more step fedora please add rclone try fedora 36 just combine them drop fedora, build musl remove unneeded quotes force rm requote use untemplated yaml seperate commands fix ref give releasing duty to releasing image remove scw download put tag in job it's not happy with the var ok this will work somehow remove quotes echo them heck it i hate ci debug reset combine pipelines check others build musl on release use debian image export tags at runtime drop musl11, fix build condition --- .woodpecker.yml | 182 ++++++++++++++++++++++++++++++++++++++ .woodpecker/.docs.yml | 27 ------ .woodpecker/.release.yml | 59 ------------ .woodpecker/.test.yml | 59 ------------ Dockerfile | 2 +- rel/files/bin/pleroma_ctl | 2 + 6 files changed, 185 insertions(+), 146 deletions(-) create mode 100644 .woodpecker.yml delete mode 100644 .woodpecker/.docs.yml delete mode 100644 .woodpecker/.release.yml delete mode 100644 .woodpecker/.test.yml diff --git a/.woodpecker.yml b/.woodpecker.yml new file mode 100644 index 000000000..0b634375c --- /dev/null +++ b/.woodpecker.yml @@ -0,0 +1,182 @@ +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 + 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 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/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/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 } -- 2.34.1 From 40f6ea51ae53eb7a884a6adf42a384a112961a14 Mon Sep 17 00:00:00 2001 From: FloatingGhost Date: Wed, 27 Jul 2022 19:42:47 +0100 Subject: [PATCH 2/4] Revert "purge ldap authenticator (#92)" This reverts commit 729f45ccd243bd01db63949ba6f51033e718f9bf. --- CHANGELOG.md | 1 - config/config.exs | 12 +- config/description.exs | 98 +++++++++++++ docs/docs/configuration/cheatsheet.md | 22 +++ docs/docs/installation/alpine_linux_en.md | 6 + lib/pleroma/user.ex | 28 ++++ lib/pleroma/web/auth/ldap_authenticator.ex | 129 +++++++++++++++++ mix.exs | 1 + .../web/o_auth/ldap_authorization_test.exs | 135 ++++++++++++++++++ 9 files changed, 430 insertions(+), 2 deletions(-) create mode 100644 lib/pleroma/web/auth/ldap_authenticator.ex create mode 100644 test/pleroma/web/o_auth/ldap_authorization_test.exs 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/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..077d5ffa6 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -660,6 +660,34 @@ 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 = Map.put_new(params, :accepts_chat_messages, true) + + params = + if Map.has_key?(params, :email) do + Map.put_new(params, :email, params[:email]) + else + params + end + + struct + |> cast(params, [ + :name, + :nickname, + :email, + :accepts_chat_messages + ]) + |> 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/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 -- 2.34.1 From 7c3e3b50fd8dae32d6eb08e0a1207542c2159757 Mon Sep 17 00:00:00 2001 From: FloatingGhost Date: Wed, 27 Jul 2022 19:48:00 +0100 Subject: [PATCH 3/4] merge build and test steps --- .woodpecker.yml | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/.woodpecker.yml b/.woodpecker.yml index 0b634375c..d728bbea8 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -50,17 +50,6 @@ pipeline: - mix local.rebar --force - mix format --check-formatted - build: - image: akkoma/ci-base:latest - <<: *on-pr-open - environment: - MIX_ENV: test - commands: - - mix local.hex --force - - mix local.rebar --force - - mix deps.get - - mix compile - test: image: akkoma/ci-base:latest <<: *on-pr-open @@ -74,6 +63,7 @@ pipeline: - mix local.hex --force - mix local.rebar --force - mix deps.get + - mix compile - mix ecto.drop -f -q - mix ecto.create - mix ecto.migrate -- 2.34.1 From f036674fffdb87e7842c3251be76d6beddc85f39 Mon Sep 17 00:00:00 2001 From: FloatingGhost Date: Wed, 27 Jul 2022 22:18:25 +0100 Subject: [PATCH 4/4] fix tests --- .woodpecker.yml | 15 +++++++++++++++ lib/pleroma/user.ex | 5 +---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/.woodpecker.yml b/.woodpecker.yml index d728bbea8..be497b531 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -50,6 +50,21 @@ pipeline: - 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 diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 077d5ffa6..2a1b5af94 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -663,8 +663,6 @@ defmodule Pleroma.User do # 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 = Map.put_new(params, :accepts_chat_messages, true) - params = if Map.has_key?(params, :email) do Map.put_new(params, :email, params[:email]) @@ -676,8 +674,7 @@ defmodule Pleroma.User do |> cast(params, [ :name, :nickname, - :email, - :accepts_chat_messages + :email ]) |> validate_required([:name, :nickname]) |> unique_constraint(:nickname) -- 2.34.1