如何测试控制器授权
How test controller authorization
我有一个类似于这个简化代码的控制器:
defmodule Web.UserController do
use Web, :controller
alias App.User
action_fallback Web.FallbackController
def authorize(conn) do
# in my code I have somo checks here
conn
|> send_resp(403, "")
|> halt()
end
def index(conn, _params) do
authorize(conn)
users = User.all
render(conn, "index.json", users: users)
end
end
test "lists all users", %{conn: conn} do
conn = get(conn, Routes.user_path(conn, :index))
users = User.all
assert conn.halted
assert json_response(conn, 403)
end
当我用休息客户端检查它时它 return 403
但在测试中它 returns 200
。我如何测试它?
测试不错,你的代码不行。
你的 authorize
函数 return 一个 conn
,但你从未在 index
函数上使用它。
当您使用休息客户端请求它时,连接正确接收
conn
|> send_resp(403, "")
但在 ExUnit 中,它得到的是 index
returns : render(conn, "index.json", users: users)
因为你还没有使用 conn
那 authorize(conn)
returns
我建议快速解决这个问题:
defmodule Web.UserController do
use Web, :controller
alias App.User
action_fallback Web.FallbackController
def authorize(conn) do
# in my code I have somo checks here
:not_authorized
end
def index(conn, _params) do
case authorize(conn) do
:not_authorized ->
conn
|> send_resp(403, "")
|> halt() # not necessary since send_resp already does it
:authorized ->
users = User.all
render(conn, "index.json", users: users)
end
end
end
更好的解决方案是制作一个用于授权目的的插件,将其添加到路由器的管道中,如果连接未授权,它将无法到达您的控制器。
既然 phoenix 支持一个叫做插件的好东西,为什么不让它们工作呢?
从多个角度来看,您的方法很糟糕:
- 您可能想在多个控制器中调用授权。
- 您总是必须在您的控制器中实现授权行为, 会使您的代码非常混乱。
- 诸如未授权或禁止之类的事情应该由回退控制器处理(它是为这样的提议而设计的)
我现在有一个类似的项目,我必须在其中实现用户角色,我的解决方案是使用自定义插件。
插件结构如下:
defmodule WebApp.Plugs.Roles do
import Plug.Conn
import Phoenix.Controller
alias WebApp.ErrorView
def init(default), do: default
def call(conn, acl) do
# check here for authorization here, in my case whether a user has a specific acl
case :authorized do
:not_authorized ->
conn
|> put_status(403)
|> put_view(ErrorView)
|> render(:"403")
|> halt()
_ -> conn
end
end
end
然后您可以通过在控制器中声明来非常轻松地将此插件用于多个端点:
plug Roles, "create_users" when action in [:create]
这个解决方案非常简单,非常可扩展并且非常实用
我有一个类似于这个简化代码的控制器:
defmodule Web.UserController do
use Web, :controller
alias App.User
action_fallback Web.FallbackController
def authorize(conn) do
# in my code I have somo checks here
conn
|> send_resp(403, "")
|> halt()
end
def index(conn, _params) do
authorize(conn)
users = User.all
render(conn, "index.json", users: users)
end
end
test "lists all users", %{conn: conn} do
conn = get(conn, Routes.user_path(conn, :index))
users = User.all
assert conn.halted
assert json_response(conn, 403)
end
当我用休息客户端检查它时它 return 403
但在测试中它 returns 200
。我如何测试它?
测试不错,你的代码不行。
你的 authorize
函数 return 一个 conn
,但你从未在 index
函数上使用它。
当您使用休息客户端请求它时,连接正确接收
conn
|> send_resp(403, "")
但在 ExUnit 中,它得到的是 index
returns : render(conn, "index.json", users: users)
因为你还没有使用 conn
那 authorize(conn)
returns
我建议快速解决这个问题:
defmodule Web.UserController do
use Web, :controller
alias App.User
action_fallback Web.FallbackController
def authorize(conn) do
# in my code I have somo checks here
:not_authorized
end
def index(conn, _params) do
case authorize(conn) do
:not_authorized ->
conn
|> send_resp(403, "")
|> halt() # not necessary since send_resp already does it
:authorized ->
users = User.all
render(conn, "index.json", users: users)
end
end
end
更好的解决方案是制作一个用于授权目的的插件,将其添加到路由器的管道中,如果连接未授权,它将无法到达您的控制器。
既然 phoenix 支持一个叫做插件的好东西,为什么不让它们工作呢? 从多个角度来看,您的方法很糟糕:
- 您可能想在多个控制器中调用授权。
- 您总是必须在您的控制器中实现授权行为, 会使您的代码非常混乱。
- 诸如未授权或禁止之类的事情应该由回退控制器处理(它是为这样的提议而设计的)
我现在有一个类似的项目,我必须在其中实现用户角色,我的解决方案是使用自定义插件。 插件结构如下:
defmodule WebApp.Plugs.Roles do
import Plug.Conn
import Phoenix.Controller
alias WebApp.ErrorView
def init(default), do: default
def call(conn, acl) do
# check here for authorization here, in my case whether a user has a specific acl
case :authorized do
:not_authorized ->
conn
|> put_status(403)
|> put_view(ErrorView)
|> render(:"403")
|> halt()
_ -> conn
end
end
end
然后您可以通过在控制器中声明来非常轻松地将此插件用于多个端点:
plug Roles, "create_users" when action in [:create]
这个解决方案非常简单,非常可扩展并且非常实用