diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/pleroma/web/auth/ldap_authenticator.ex | 143 | ||||
| -rw-r--r-- | lib/pleroma/web/auth/pleroma_authenticator.ex | 9 | ||||
| -rw-r--r-- | lib/pleroma/web/oauth/oauth_controller.ex | 8 | 
3 files changed, 154 insertions, 6 deletions
| diff --git a/lib/pleroma/web/auth/ldap_authenticator.ex b/lib/pleroma/web/auth/ldap_authenticator.ex new file mode 100644 index 000000000..88217aab8 --- /dev/null +++ b/lib/pleroma/web/auth/ldap_authenticator.ex @@ -0,0 +1,143 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Auth.LDAPAuthenticator do +  alias Pleroma.User + +  require Logger + +  @behaviour Pleroma.Web.Auth.Authenticator + +  @connection_timeout 10_000 +  @search_timeout 10_000 + +  def get_user(%Plug.Conn{} = conn) do +    if Pleroma.Config.get([:ldap, :enabled]) do +      {name, password} = +        case conn.params do +          %{"authorization" => %{"name" => name, "password" => password}} -> +            {name, password} + +          %{"grant_type" => "password", "username" => name, "password" => password} -> +            {name, password} +        end + +      case ldap_user(name, password) do +        %User{} = user -> +          {:ok, user} + +        {:error, {:ldap_connection_error, _}} -> +          # When LDAP is unavailable, try default authenticator +          Pleroma.Web.Auth.PleromaAuthenticator.get_user(conn) + +        error -> +          error +      end +    else +      # Fall back to default authenticator +      Pleroma.Web.Auth.PleromaAuthenticator.get_user(conn) +    end +  end + +  def handle_error(%Plug.Conn{} = _conn, error) do +    error +  end + +  def auth_template, do: nil + +  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 User.get_by_nickname_or_email(name) do +          %User{} = user -> +            user + +          _ -> +            register_user(connection, base, uid, name, password) +        end + +      error -> +        error +    end +  end + +  defp 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()}, +           {:attributes, ['mail', 'email']}, +           {: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 +          _ -> +            Logger.error("Could not find LDAP attribute mail: #{inspect(attributes)}") +            {:error, :ldap_registration_missing_attributes} +        end + +      error -> +        error +    end +  end +end diff --git a/lib/pleroma/web/auth/pleroma_authenticator.ex b/lib/pleroma/web/auth/pleroma_authenticator.ex index 333446bef..94a19ad49 100644 --- a/lib/pleroma/web/auth/pleroma_authenticator.ex +++ b/lib/pleroma/web/auth/pleroma_authenticator.ex @@ -9,7 +9,14 @@ defmodule Pleroma.Web.Auth.PleromaAuthenticator do    @behaviour Pleroma.Web.Auth.Authenticator    def get_user(%Plug.Conn{} = conn) do -    %{"authorization" => %{"name" => name, "password" => password}} = conn.params +    {name, password} = +      case conn.params do +        %{"authorization" => %{"name" => name, "password" => password}} -> +          {name, password} + +        %{"grant_type" => "password", "username" => name, "password" => password} -> +          {name, password} +      end      with {_, %User{} = user} <- {:user, User.get_by_nickname_or_email(name)},           {_, true} <- {:checkpw, Pbkdf2.checkpw(password, user.password_hash)} do diff --git a/lib/pleroma/web/oauth/oauth_controller.ex b/lib/pleroma/web/oauth/oauth_controller.ex index ec70b7ccc..d69383d40 100644 --- a/lib/pleroma/web/oauth/oauth_controller.ex +++ b/lib/pleroma/web/oauth/oauth_controller.ex @@ -5,7 +5,6 @@  defmodule Pleroma.Web.OAuth.OAuthController do    use Pleroma.Web, :controller -  alias Comeonin.Pbkdf2    alias Pleroma.Repo    alias Pleroma.User    alias Pleroma.Web.Auth.Authenticator @@ -126,11 +125,10 @@ defmodule Pleroma.Web.OAuth.OAuthController do    def token_exchange(          conn, -        %{"grant_type" => "password", "username" => name, "password" => password} = params +        %{"grant_type" => "password"} = params        ) do -    with %App{} = app <- get_app_from_request(conn, params), -         %User{} = user <- User.get_by_nickname_or_email(name), -         true <- Pbkdf2.checkpw(password, user.password_hash), +    with {_, {:ok, %User{} = user}} <- {:get_user, Authenticator.get_user(conn)}, +         %App{} = app <- get_app_from_request(conn, params),           {:auth_active, true} <- {:auth_active, User.auth_active?(user)},           scopes <- oauth_scopes(params, app.scopes),           [] <- scopes -- app.scopes, | 
