Как использовать соединение с сеансом в phoenix?
У меня есть плагин проверки подлинности, и я хочу проверить свои контроллеры. Проблема в том, что строка в этой вилке имеет
user_id = get_session(conn, :user_id)
И всегда, когда я использую этот метод (раньше я использовал грязный хак, но я больше не хочу этого делать):
@session Plug.Session.init([
store: :cookie,
key: "_app",
encryption_salt: "secret",
signing_salt: "secret",
encrypt: false
])
user = MyApp.Factory.create(:user)
conn()
|> put_req_header("accept", "application/vnd.api+json")
|> put_req_header("content-type", "application/vnd.api+json")
|> Map.put(:secret_key_base, String.duplicate("abcdefgh", 8))
|> Plug.Session.call(@session)
|> fetch_session
|> put_session(:user_id, user.id)
Я отправляю запрос патча, используя этот conn, и его session_id пользователя равен нулю. Результаты IO.puts conn
в моей плагин:
%Plug.Conn{adapter: {Plug.Adapters.Test.Conn, :...}, assigns: %{},
before_send: [#Function<0.111117999/1 in Plug.Session.before_send/2>,
#Function<0.110103833/1 in JaSerializer.ContentTypeNegotiation.set_content_type/2>,
#Function<1.55011211/1 in Plug.Logger.call/2>,
#Function<0.111117999/1 in Plug.Session.before_send/2>], body_params: %{},
cookies: %{}, halted: false, host: "www.example.com", method: "PATCH",
owner: #PID<0.349.0>,
params: %{"data" => %{"attributes" => %{"action" => "start"}}, "id" => "245"},
path_info: ["api", "tasks", "245"], peer: {{127, 0, 0, 1}, 111317}, port: 80,
private: %{MyApp.Router => {[], %{}}, :phoenix_endpoint => MyApp.Endpoint,
:phoenix_format => "json-api", :phoenix_pipelines => [:api],
:phoenix_recycled => true,
:phoenix_route => #Function<4.15522358/1 in MyApp.Router.match_route/4>,
:phoenix_router => MyApp.Router, :plug_session => %{},
:plug_session_fetch => :done, :plug_session_info => :write,
:plug_skip_csrf_protection => true}, query_params: %{}, query_string: "",
remote_ip: {127, 0, 0, 1}, req_cookies: %{},
req_headers: [{"accept", "application/vnd.api+json"},
{"content-type", "application/vnd.api+json"}], request_path: "/api/tasks/245",
resp_body: nil, resp_cookies: %{},
resp_headers: [{"cache-control", "max-age=0, private, must-revalidate"},
{"x-request-id", "d00tun3s9d7fo2ah2klnhafvt3ks4pbj"}], scheme: :http,
script_name: [],
secret_key_base: "npvJ1fWodIYzJ2eNnJmC5b1LecCTsveK4/mj7akuBaLdeAr2KGH4gwohwHsz8Ony",
state: :unset, status: nil}
Что мне нужно сделать для решения этой проблемы и проверки подлинности?
UPDATE. Подключить аутентификацию
defmodule MyApp.Plug.Authenticate do
import Plug.Conn
import Phoenix.Controller
def init(default), do: default
def call(conn, _) do
IO.puts inspect get_session(conn, :user_id)
IO.puts conn
user_id = get_session(conn, :user_id)
if user_id do
current_user = MyApp.Repo.get(MyApp.Task, user_id)
assign(conn, :current_user, current_user)
else
conn
|> put_status(401)
|> json(%{})
|> halt
end
end
end
router (я отрубил некоторые части отсюда):
defmodule MyApp.Router do
use MyApp.Web, :router
pipeline :api do
plug :accepts, ["json-api"] # this line and 3 below are under JaSerializer package responsibility
plug JaSerializer.ContentTypeNegotiation
plug JaSerializer.Deserializer
plug :fetch_session
plug MyApp.Plug.Authenticate # this one
end
scope "/api", MyApp do
pipe_through :api
# tasks
resources "/tasks", TaskController, only: [:show, :update]
end
end
Ответы
Ответ 1
Это можно решить гораздо проще, минуя сеансы в тестах. Идея состоит в том, чтобы напрямую привязать current_user
к тестам conn и в прокси-сервере проверки подлинности, который выбирает пользователя из сеанса при назначении назначения current_user
.
Это, очевидно, не позволяет проверять подлинность, но тестирование там должно быть намного проще, чем через весь стек.
# in the authentication plug
def call(%{assigns: %{current_user: user}} = conn, opts) when user != nil do
conn
end
def call(conn, opts) do
# handle fetching user from session
end
Это позволяет просто выполнить assign(conn, :current_user, user)
в тестах для аутентификации соединения.
Ответ 2
Поскольку вы вызываете свой сеанс до fetch_session/2
, поэтому в вашем подключаемом модуле проверки подлинности get_session/2
будет возвращен nil
Измените свой винт проверки подлинности, чтобы выполнить проверку:
defmodule MyApp.Plug.Authenticate do
import Plug.Conn
import Phoenix.Controller
alias MyApp.{Repo, User}
def init(opts), do: opts
def call(conn, _opts) do
if user = get_user(conn) do
assign(conn, :current_user, user)
else
conn
|> put_status(401)
|> put_flash(:error, "You must be logged in!")
|> halt
end
end
def get_user(conn) do
case conn.assigns[:current_user] do
nil ->
case get_session(conn, :user_id) do
id -> fetch_user(id)
nil -> nil
end
user -> user
end
end
defp fetch_user(id), do: Repo.get!(User, id)
end
Теперь вы можете проверить свою пробку следующим образом:
defmodule MyApp.Plug.AuthenticateTest do
use ExUnit.Case, async: true
use Plug.Test
import Phoenix.ConnTest
alias MyApp.Plug.Authenticate
@endpoint MyApp.Endpoint
@session Plug.Session.init([
store: :cookie,
key: "_app",
encryption_salt: "secret",
signing_salt: "secret",
encrypt: false
])
setup do
user = MyApp.Factory.create(:user)
conn = build_conn()
|> put_req_header("accept", "application/vnd.api+json")
|> put_req_header("content-type", "application/vnd.api+json")
|> Map.put(:secret_key_base, String.duplicate("abcdefgh", 8))
|> Plug.Session.call(@session)
|> fetch_session
|> put_session(:user_id, user.id)
{:ok, conn: conn, user: user}
end
test "get_user returns where it is set in session", %{conn: conn, user: user} do
assert Authenticate.get_user(conn) == user
end
end
И, наконец, вы можете проверить свой контроллер следующим образом:
setup do
user = MyApp.Factory.create(:user)
{:ok, user: user}
end
test "GET /login", %{user: user} do
conn = build_conn()
|> assign(:current_user, user)
|> get("/login")
assert html_response(conn, 200) =~ "Successfull login"
end
Есть аналогичный вопрос:
как установить сессию в настройке, когда я тестирую действие phoenix, которое нужно user_id в сеансе?
И лучше, когда вы хотите, чтобы пользовательский для теста загружал его в conn.private
и читал его из приватного в вашем подключаемом модуле проверки подлинности.
Вы должны взглянуть на изменение.
Надеюсь, что вам помогут!