Reputation: 5535
I have a controller similar to this simplified code:
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
When I check it with rest client it return 403
but in the test it returns 200
. How I can test it?
Upvotes: 0
Views: 252
Reputation: 2554
Since phoenix supports a wonderful thing called plugs, why not put them to work? Your approach is bad from multiple points of view:
I have a similar project now where I have to implement user roles and the solution I came with is to use a custom plug. The structure of the plug is the following:
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
And then you can use this plug very easily for multiple endpoints by declaring in your controller :
plug Roles, "create_users" when action in [:create]
This solution is very easy, very scalable and ver
Upvotes: 1
Reputation: 7506
The test is good, your code is not.
your authorize
function return a conn
, but you never use it on the index
function.
When you request it with a rest client, the connection receives correctly the
conn
|> send_resp(403, "")
But in ExUnit, it gets what index
returns : render(conn, "index.json", users: users)
Since you haven't use the conn
that authorize(conn)
returns
My suggestion to fix this issue rapidly :
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
A better solution would be to make a Plug for authorization purpose, add it to a pipeline in your router, it will not reach your controller if the connection is not authorized.
Upvotes: 1