使用 LDAP 进行身份验证时使用 Guardian.Plug.EnsureAuthenticated 通过测试
Passing tests with Guardian.Plug.EnsureAuthenticated when authenticating with LDAP
我按照 http://rny.io/elixir/phoenix/ldap/2016/09/20/ldap-authenication-with-phoenix.html 上的指南在 Phoenix 中使用 Guardian 设置 LDAP 身份验证。我对 Phoenix 和 Elixir 还很陌生,所以我正在经历设置和测试的过程。
我已经按照指南进行了所有工作,但是,在编写控制器测试时,我不知道如何通过 Guardian.Plug.EnsureAuthenticated。我遵循了此处找到的几个指南,但似乎没有任何效果。
是否有人使用 exLDAP 和 Guardian 设置了 LDAP 身份验证以及用于测试的正确用户登录可以提供一些指导?任何帮助将不胜感激。
以下是我的设置:
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
结束
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
我创建了一个session_controller_text.ex如下:
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
上次测试失败,因为它将我重定向到登录页面。
当然,出于测试目的,您应该使用 bypass_through
跳过身份验证过程。在 docs.
中阅读更多内容
我按照 http://rny.io/elixir/phoenix/ldap/2016/09/20/ldap-authenication-with-phoenix.html 上的指南在 Phoenix 中使用 Guardian 设置 LDAP 身份验证。我对 Phoenix 和 Elixir 还很陌生,所以我正在经历设置和测试的过程。
我已经按照指南进行了所有工作,但是,在编写控制器测试时,我不知道如何通过 Guardian.Plug.EnsureAuthenticated。我遵循了此处找到的几个指南,但似乎没有任何效果。
是否有人使用 exLDAP 和 Guardian 设置了 LDAP 身份验证以及用于测试的正确用户登录可以提供一些指导?任何帮助将不胜感激。
以下是我的设置:
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
结束
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
我创建了一个session_controller_text.ex如下:
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
上次测试失败,因为它将我重定向到登录页面。
当然,出于测试目的,您应该使用 bypass_through
跳过身份验证过程。在 docs.