Passing tests with Guardian.Plug.EnsureAuthenticated when authenticating with LDAP

466 views Asked by At

I followed the guide at http://rny.io/elixir/phoenix/ldap/2016/09/20/ldap-authenication-with-phoenix.html to setup LDAP authentication with Guardian in Phoenix. I am fairly new to Phoenix and Elixir so I am going through the motions of setting things up and testing.

I have everything working as per the guide but, I cannot figure out how to get past Guardian.Plug.EnsureAuthenticated when writing controller tests. I have followed several guides found on here but nothing seems to be working.

Has anyone setup LDAP authentication with exLDAP and Guardian and the proper user logins for testing that can offer some guidance? Any help would be appreciated.

Below is my setup:

lib/ldap_example/guardian_serializer.ex

defmodule LdapExample.GuardianSerializer do
 @behaviour Guardian.Serializer
 alias LdapExample.User
 alias LdapExample.Repo

 def for_token(user = %User{}), do: { :ok, "User:#{user.id}" }
 def for_token(_), do: { :error, "Unknown resource type" }

 def from_token("User:" <> id), do: { :ok, Repo.get(User, id) }
 def from_token(_), do: { :error, "Unknown resource type" }
end

lib/ldap_example/ldap.ex

    defmodule LdapExample.Ldap do

    def authenticate(uid, password) do
      {:ok, ldap_conn} = Exldap.open
    bind = "uid=#{uid},dc=example,dc=com"
    case Exldap.verify_credentials(ldap_conn, bind, password) do
      :ok -> :ok
      _ -> {:error, "Invalid username / password"}
    end
  end



def get_by_uid(uid) do
    {:ok, ldap_conn} = Exldap.connect
    {:ok, search_results} = Exldap.search_field(ldap_conn, "uid", uid)
    case search_results do
      [] -> {:error, "Could not find user with uid #{uid}"}
      _ -> search_results |> Enum.fetch(0)
    end
  end



def to_map(entry) do
    username = Exldap.search_attributes(entry, "uid")
    name = Exldap.search_attributes(entry, "cn")
    email = Exldap.search_attributes(entry, "mail")
    %{username: username, name: name, email: email}
  end
end

web/controllers/session_controller.ex

defmodule LdapExample.SessionController do
  use LdapExample.Web, :controller
  alias LdapExample.{User, Repo, Ldap}

  def new(conn, _params) do
    render conn, "new.html", changeset: User.login_changeset
  end



def create(conn, %{"user" => params}) do
    username = params["username"]
    password = params["password"]
    case Ldap.authenticate(username, password) do
      :ok -> handle_sign_in(conn, username)
      _   -> handle_error(conn)
    end
  end

defp handle_sign_in(conn, username) do
  {:ok, user} = insert_or_update_user(username)
  conn
  |> put_flash(:info, "Logged in.")
  |> Guardian.Plug.sign_in(user)
  |> redirect(to: page_path(conn, :index))
end

defp insert_or_update_user(username) do
  {:ok, ldap_entry} = Ldap.get_by_uid(username)
  user_attributes = Ldap.to_map(ldap_entry)
  user = Repo.get_by(User, username: username)
  changeset =
    case user do
      nil -> User.changeset(%User{}, user_attributes)
      _ -> User.changeset(user, user_attributes)
    end
  Repo.insert_or_update changeset
end

defp handle_error(conn) do
  conn
  |> put_flash(:error, "Wrong username or password")
  |> redirect(to: session_path(conn, :new))
end

def delete(conn, _params) do
  Guardian.Plug.sign_out(conn)
  |> put_flash(:info, "Logged out successfully.")
  |> redirect(to: "/")
end

end

web/model/user.ex

defmodule LdapExample.User do
 use LdapExample.Web, :model

 schema "users" do
  field :username, :string
  field :name, :string
  field :email, :string
  field :password, :string, virtual: true
  timestamps()
end



def changeset(struct, params \\ %{}) do
    struct
    |> cast(params, [:username, :name, :email])
    |> validate_required([:username, :name, :email])
  end



def login_changeset do
    %__MODULE__{} |> cast(%{}, ~w(username password), ~w())
  end
end

I created a session_controller_text.ex as follows:

defmodule LdapExample.SessionControllerTest do
  use LdapExample.ConnCase
  alias LdapExample.User



test "Get to Login page", %{conn: conn} do
    conn = get conn, session_path(conn, :new)
    assert html_response(conn, 200) =~ "Sign In"
  end

test "shows page only when logged in", %{conn: conn} do
    conn = get conn, page_path(conn, :index)
    assert html_response(conn, 200) =~ "Hello"
  end
end

The last test fails as it redirects me to the login page.

1

There are 1 answers

3
PatNowak On BEST ANSWER

Certainly you should use bypass_through to skip the authentication process for your test purposes. Read more in docs.