Reputation: 151
I am trying to work on a Role Based Access control Pheonix API. In which the user will sign in and a session will be created. After logging in he can create a post the post created will be assigned to that user_id. I have encountered this problem the user_id is coming like a string instead of the value inside it.
I have tried String.to_integer(user_id) but it is giving an error because user_id is coming like a string, not a variable storing an id.
My router
scope "/", PxblogWeb do
pipe_through :browser
get "/", PageController, :index
resources "/users", UserController do
resources "/posts", PostController
end
resources "/sessions", SessionController, only: [:new, :create, :delete]
end
scope "/" do
resources "/sessions", SessionController, only: [:new, :create, :delete]
end
# Other scopes may use custom stacks.
scope "/api", PxblogWeb do
pipe_through :api
get "/users/read", UserController, :index
end
new.html.eex
<h1>New Post</h1>
<%= render "form.html", Map.put(assigns, :action, Routes.user_post_path(@conn, :create, :user_id)) %>
<span><%= link "Back", to: Routes.user_post_path(@conn, :index, :user_id) %></span>
post_controller.ex
plug :assign_user
plug :authorize_user when action in [:new, :create, :update, :edit, :delete]
defp assign_user(conn, _opts) do
case conn.params do
%{"user_id" => user_id} ->
case Repo.get(Pxblog.Post.User, user_id) do
nil -> invalid_user(conn)
user -> assign(conn, :user, user)
end
_ -> invalid_user(conn)
end
end
defp invalid_user(conn) do
conn
|> put_flash(:error, "Invalid user!")
|> redirect(to: Routes.page_path(conn, :index))
|> halt
end
defp authorize_user(conn, _) do
user = get_session(conn, :current_user)
if user && (Integer.to_string(user.id) == conn.params["user_id"] || Pxblog.RoleChecker.is_admin?(user)) do
conn
else
conn
|> put_flash(:error, "You are not authorized to modify that post!")
|> redirect(to: Routes.page_path(conn, :index))
|> halt()
end
end
def index(conn, _params) do
posts = Repo.all(assoc(conn.assigns[:user], :posts))
render(conn, "index.html", posts: posts)
end
def new(conn, _params) do
changeset =
conn.assigns[:user]
|> build_assoc(:posts)
|> Pxblog.Learn.Post.changeset(%{})
render(conn, "new.html", changeset: changeset)
end
def create(conn, %{"post" => post_params}) do
changeset =
conn.assigns[:user]
|> build_assoc(:posts)
|> Pxblog.Learn.Post.changeset(post_params)
case Repo.insert(changeset) do
{:ok, _post} ->
conn
|> put_flash(:info, "Post created successfully.")
|> redirect(to: Routes.user_post_path(conn, :index, conn.assigns[:user]))
{:error, changeset} ->
render(conn, "new.html", changeset: changeset)
end
end
session_controller.ex
defmodule PxblogWeb.SessionController do
use PxblogWeb, :controller
alias Pxblog.Post.User
alias Pxblog.Post
alias Pxblog.Repo
import Comeonin.Bcrypt, only: [checkpw: 2, dummy_checkpw: 0]
plug :scrub_params, "user" when action in [:create]
def new(conn, _params) do
render conn, "new.html", changeset: Pxblog.Post.User.changeset(%Pxblog.Post.User{}, %{})
end
def create(conn, %{"user" => %{"username" => username, "password" => password}})
when not is_nil(username) and not is_nil(password) do
user = Repo.get_by(Pxblog.Post.User, username: username)
sign_in(user, password, conn)
end
def create(conn, _) do
failed_login(conn)
end
defp failed_login(conn) do
dummy_checkpw()
conn
|> put_session(:current_user, nil)
|> put_flash(:error, "Invalid username/password combination!")
|> redirect(to: Routes.page_path(conn, :index))
|> halt()
end
defp sign_in(user, _password, conn) when is_nil(user) do
failed_login(conn)
end
defp sign_in(user, password, conn) do
if checkpw(password, user.password_digest) do
conn
|> put_session(:current_user, %{id: user.id, username: user.username, role_id: user.role_id})
|> put_flash(:info, "Sign in successful!")
|> redirect(to: Routes.page_path(conn, :index))
else
failed_login(conn)
end
end
def delete(conn, _params) do
conn
|> delete_session(:current_user)
|> put_flash(:info, "Signed out successfully!")
|> redirect(to: Routes.page_path(conn, :index))
end
end
Upvotes: 0
Views: 935
Reputation: 48589
The error in your title suggests that somewhere there is a string that looks like:
"some string user_id"
instead of:
"some string #{user_id}"
And where
in the error message indicates a db query, so:
"...where id=user_id"
instead of
"...where id=#{user_id}"
=====
In your new
action, you have:
def new(conn, _params) do
changeset =
conn.assigns[:user]
|> build_assoc(:posts)
|> Pxblog.Learn.Post.changeset(%{})
render(conn, "new.html", changeset: changeset)
end
The render(
) call makes the variable @changeset
available in the template (as well as @conn
by elixir magic). Here's your template:
<h1>New Post</h1>
<%= render "form.html",
Map.put(assigns, :action,
Routes.user_post_path(@conn, :create, :user_id)
)
%>
<span><%= link "Back",
to: Routes.user_post_path(@conn, :index, :user_id) %>
</span>
1) You don't use @changeset
in the template, so specifying changeset: changeset
in render() was unnecessary.
2) I think that each of the :user_id
atoms should be a variable instead, which contains the actual user_id--a variable that you should create in the action and specify in render(), like you did with changeset
.
When you interpolate an atom into a string (rather than a variable), you get:
iex(2)> "...where user=#{:user_id}"
"...where user=user_id"
And the error message is saying that the substring "user_id" cannot be converted to an :id
type. I think Phoenix must do a database query to construct the user_post_path
.
If you do:
$ iex -S mix
then:
iex(1)> h <YourApp>Web.Router.Helpers.user_post_path
def user_post_path(conn_or_endpoint, action, user_id)
def user_post_path(conn_or_endpoint, action, user_id, params)
def user_post_path(conn_or_endpoint, action, user_id, id, params)
iex displays the actual defs that were created for your path helper. I think that if the argument for user_id
was supposed to be the atom :user_id
, then those functions would pattern match on the atom, like this:
def user_post_path(conn_or_endpoint, action, :user_id)
def user_post_path(conn_or_endpoint, action, :user_id, params)
def user_post_path(conn_or_endpoint, action, :user_id, id, params)
Upvotes: 1