[#923] Merge remote-tracking branch 'remotes/upstream/develop' into twitter_oauth
authorIvan Tashkinov <ivant.business@gmail.com>
Mon, 18 Mar 2019 07:26:41 +0000 (10:26 +0300)
committerIvan Tashkinov <ivant.business@gmail.com>
Mon, 18 Mar 2019 07:26:41 +0000 (10:26 +0300)
# Conflicts:
# config/config.exs
# lib/pleroma/web/auth/pleroma_authenticator.ex

1  2 
config/config.exs
lib/pleroma/user.ex
lib/pleroma/web/auth/ldap_authenticator.ex
lib/pleroma/web/auth/pleroma_authenticator.ex
lib/pleroma/web/oauth/oauth_controller.ex
lib/pleroma/web/router.ex
mix.exs

index 28867ed4c8e86405d8cf2f03064f56d0f263fb42,ccdd357771dc0057c0f8723eaa53a60895dbae2c..6839b489bb4d7f582e1de2b3c98e6903c72c81dc
@@@ -370,21 -370,17 +370,32 @@@ config :auto_linker
      rel: false
    ]
  
+ 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"
 +config :pleroma, :auth, oauth_consumer_enabled: false
 +
 +config :ueberauth,
 +       Ueberauth,
 +       base_path: "/oauth",
 +       providers: [
 +         twitter:
 +           {Ueberauth.Strategy.Twitter,
 +            [callback_params: ~w[client_id redirect_uri scope scopes]]}
 +       ]
 +
 +config :ueberauth, Ueberauth.Strategy.Twitter.OAuth,
 +  consumer_key: System.get_env("TWITTER_CONSUMER_KEY"),
 +  consumer_secret: System.get_env("TWITTER_CONSUMER_SECRET")
 +
  # Import environment specific config. This must remain at the bottom
  # of this file so it overrides the configuration defined above.
  import_config "#{Mix.env()}.exs"
Simple merge
index 0000000000000000000000000000000000000000,88217aab84dd533e632c2818bd823d706efcdcb7..6c65cff27baab5248e756d500ee03bcdf11ec2eb
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,143 +1,145 @@@
 -  def get_user(%Plug.Conn{} = conn) do
+ # 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
 -        case conn.params do
++  def get_user(%Plug.Conn{} = conn, params) do
+     if Pleroma.Config.get([:ldap, :enabled]) do
+       {name, password} =
 -          Pleroma.Web.Auth.PleromaAuthenticator.get_user(conn)
++        case 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)
++          Pleroma.Web.Auth.PleromaAuthenticator.get_user(conn, params)
+         error ->
+           error
+       end
+     else
+       # Fall back to default authenticator
++      Pleroma.Web.Auth.PleromaAuthenticator.get_user(conn, params)
+     end
+   end
++  def get_or_create_user_by_oauth(conn, params), do: get_user(conn, params)
++
+   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
index 5583f41a92092a7b275ab418b4b8dafc83361b24,94a19ad49d97eaf0dd21b543e561036d2fdb2cce..2e2bcfb702063ad0d32e1ab24acb3c72040f20b3
@@@ -8,9 -8,16 +8,16 @@@ defmodule Pleroma.Web.Auth.PleromaAuthe
  
    @behaviour Pleroma.Web.Auth.Authenticator
  
-   def get_user(%Plug.Conn{} = _conn, %{
-         "authorization" => %{"name" => name, "password" => password}
-       }) do
 -  def get_user(%Plug.Conn{} = conn) do
++  def get_user(%Plug.Conn{} = _conn, params) do
+     {name, password} =
 -      case conn.params do
++      case 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
        {:ok, user}
      end
    end
  
-   def get_user(%Plug.Conn{} = _conn, _params), do: {:error, :missing_credentials}
 +  def get_or_create_user_by_oauth(
 +        %Plug.Conn{assigns: %{ueberauth_auth: %{provider: provider, uid: uid} = auth}},
 +        _params
 +      ) do
 +    user = User.get_by_auth_provider_uid(provider, uid)
 +
 +    if user do
 +      {:ok, user}
 +    else
 +      info = auth.info
 +      email = info.email
 +      nickname = info.nickname
 +
 +      # TODO: FIXME: connect to existing (non-oauth) account (need a UI flow for that) / generate a random nickname?
 +      email =
 +        if email && User.get_by_email(email) do
 +          nil
 +        else
 +          email
 +        end
 +
 +      nickname =
 +        if nickname && User.get_by_nickname(nickname) do
 +          nil
 +        else
 +          nickname
 +        end
 +
 +      new_user =
 +        User.oauth_register_changeset(
 +          %User{},
 +          %{
 +            auth_provider: to_string(provider),
 +            auth_provider_uid: to_string(uid),
 +            name: info.name,
 +            bio: info.description,
 +            email: email,
 +            nickname: nickname
 +          }
 +        )
 +
 +      Pleroma.Repo.insert(new_user)
 +    end
 +  end
 +
 +  def get_or_create_user_by_oauth(%Plug.Conn{} = _conn, _params),
 +    do: {:error, :missing_credentials}
 +
    def handle_error(%Plug.Conn{} = _conn, error) do
      error
    end
index d39c4a7136034f79fc32727f24e578ff60e4defb,d151efe9eb1fda98467517bb9e4594e0831f2018..588933d31f2f9d8b9f52da37cbf1d561a35de3b9
@@@ -175,11 -127,10 +176,10 @@@ defmodule Pleroma.Web.OAuth.OAuthContro
  
    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)},
++    with {_, {:ok, %User{} = user}} <- {:get_user, Authenticator.get_user(conn, params)},
+          %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,
Simple merge
diff --cc mix.exs
Simple merge