How to add a plug-in router in Phoenix Elixir

I created authentication for user resources and it works fine, but now I want to use the authenticate function in user_controller.ex to project_controller.ex.

If I copy the private function from user_controller to project_controller, not authentication applies to project resources, but I don’t want to copy this authentication function in every controller. I need to know what is the best way to do this code. I think the router It is a good place to add authentication plugins, but I need to know where I should add the code.

router.ex

defmodule Auth.Router do< br /> use Auth.Web, :router

pipeline :browser do
plug :accepts, ["html"]
plug :fetch_session
plug :fetch_flash< br /> plug :protect_from_forgery
plug :put_secure_browser_headers
plug Auth.Auth, repo: Auth.Repo
end

pipeline :api do
plug: accepts, ["json"]
end

scope "/", Auth do
pipe_through :browser # Use the default browser stack

get " /", PageController, :index
resources "/users", UserController
resources "/sessions", SessionController, only: [:new, :create, :delete]
resources "/projects ", ProjectController
end
end

user_controller.ex

defmodule Auth.UserController do
use Auth.Web, :controller

plug :authenticate when action in [:index, :show]

alias Auth.User

plug :scrub_params, "user" when action in [ :create, :update]

def index(conn, _params) do
users = Repo.all(User)
render(conn, "index.html", users: users)
end

def new(conn, _params) do
changeset = User.changeset(%User{})
render(conn, "new.html ", changeset: changeset)
end

def create(conn, %{"user" => user_params}) do
changeset = User.registration_changeset(%User{}, user_params)

case Repo.insert(changeset) do
{:ok, user} ->
conn
|> Auth.Auth.login(user)< br /> |> put_flash(:info, "#{user.username} created successfully.")
|> redirect(to: user_path(conn, :index))
{:error, changeset}->
render(conn, "new.html", changeset: changeset)
end
end

def show(conn, %{"id" => id}) do
user = Repo.get!(User, id)
render(conn, "show.html", user: user)
end

def edit(conn, %{"id" => id}) do
user = Repo.get!(User, id)
changeset = User.changeset(user)
render(conn , "edit.html", user: user, changeset: changeset)
end

def update(conn, %{"id" => id, "user" => user_params}) do
user = Repo.get!(User, id)
changeset = User.changeset(user, user_params)

case Repo.update(changeset) do
{:ok, user} ->
conn
|> put_flash(:info, "User updated successfully.")
|> redirect(to: user_path(conn, :show, user) )
{:error, changeset} ->
render(conn, "edit.html", user: user, changeset: changeset)
end
end
< br /> def delete(conn, %{"id" => id}) do
user = Repo.get!(User, id)
Repo.delete!(user)
conn
|> put_flash(:info, "User deleted successfully.")
|> redirect (to: user_path(conn, :index))
end

defp authenticate(conn, _opts) do
if conn.assigns.current_user do
conn
else
conn
|> put_flash(:error, "You must be login to access that page.")
|> redirect(to: page_path(conn, :index))< br /> |> halt()
end
end
end

model/user.ex

defmodule Auth.User do
use Auth.Web, :model

schema "users" do
field :username, :string
field :password_hash, :string
field :password, :string, virtual: true

timestamps
end

def changeset(model, params \ :empty) do
model
|> cast(params, ~w(username), [])
|> validate_length(:username, min: 3, max: 20)

end

def registration_change set(model, params) do
model
|> changeset(params)
|> cast(params, ~w(password), [])
|> validate_length(: password, min: 6, max: 100)
|> put_pass_hash()
end

defp put_pass_hash(changeset) do
case changeset do
% Ecto.Changeset{valid?: true, changes: %{password: pass}} ->
put_change(changeset, :password_hash, Comeonin.Bcrypt.hashpwsalt(pass))
_ ->
changeset
end
end
end

controller/ auth.ex

defmodule Auth.Auth do< br /> import Plug.Conn
import Comeonin.Bcrypt, only: [checkpw: 2]

def init(opts) do
Keyword.fetch!(opts, :repo )
end

def call(conn, repo) do
user_id = get_session(conn, :user_id)
user = user_id && repo.get(Auth.User , user_id)
assign(conn, :current_user, user)
end

def login(conn, user) do
conn
|> assign( :curre nt_user, user)
|> put_session(:user_id, user.id)
|> configure_session(renew: true)
end

def login_by_username_and_pass(conn, username , given_pass, opts) do
repo = Keyword.fetch!(opts, :repo)
user = repo.get_by(Auth.User, username: username)
cond do
user && checkpw(given_pass, user.password_hash) ->
{:ok, login(conn, user)}
user ->
{:error, :unauthorized, conn}
true ->
{:error, :not_found, conn}
end
end

def logout(conn) do
# configure_session(conn, drop: true)
delete_session(conn, :user_id)
end

end

controller/ project_controller.ex

< /p>

defmodule Auth.ProjectController do
use Auth.Web, :controller

plug :authenticate when action in [:index, :new, :show]

alias Auth.Project

plug :scrub_params, "project" when action in [:create,: update]

def index(conn, _params) do
projects = Repo.all(Project)
render(conn, "index.html", projects: projects)
end

def new(conn, _params) do
changeset = Project.changeset(%Project{})
render(conn, "new.html", changeset: changeset)
end

def create(conn, %{"project" => project_params}) do
changeset = Project.changeset(%Project{}, project_params)

case Repo.insert(changeset) do
{:ok, _project} ->
conn
|> put_flash(:info, "Project created successfully.")< br /> |> redirect(to: project_path(conn, :index))
{:error, changeset} ->
render(conn, "new.html", changeset: changeset)
end
end

def show(conn, %{"id" => id}) do
project = Repo.get!(Project, id)
render(conn, "show.html", project: project)
end

def edit(conn, %{"id" => id}) do
project = Repo . get!(Project, id)
changeset = Project.changeset(project)
render(conn, "edit.html", project: project, changeset: changeset)
end

def update(conn, %{"id" => id, "project" => project_params}) do
project = Repo.get!(Project, id)
changeset = Project. changeset(project, project_params)

case Repo.update(changeset) do
{:ok, project} ->
conn
|> put_flash(:info, "Project updated successfully.")
|> redirect(to: project_path(conn, :show, project))
{:error, changeset} ->
render(conn, "edit. html", project: project, changeset: changeset)
end
end

def delete(conn, %{"id" => id}) do
project = Repo.get!(Project, id)

# Here we use delete! (with a bang) because we expect
# it to always work (and if it does not, it will raise).
Repo.delete!(project)

conn
|> put_flash(:info, "Project deleted successfully.")
|> redirect(to: project_path(conn, :index))
end


# defp authenticate(conn, _opts) do
# if conn.assigns.current_user do
# conn
# else
# conn
# |> put_flash(:error, "You must be login to access that page.")
# |> redirect(to: page_path(conn, :index))
# |> halt()
# end
# end
end

This is a fairly common mode. First you need your Authenticate plugin:

defmodule Auth.Plug.Authenticate do
@behaviour Plug
import Plug.Conn
import Phoenix.Controller, only: [put_flash : 3, redirect: 2]

def init(opts), do: opts

def call(conn, _opts) do
if conn.assigns.current_user do
conn
else
conn
|> put_flash(:error, "You must be login to access that page.")
|> redirect(to: Auth .Router.Helpers .page_path(conn, :index))
|> halt()
end
end
end

Then in your router, you can:

defmodule Auth.Router do
use Auth.Web, :router

pipeline :browser do
plug :accepts, ["html"]
plug :fetch_session
plug :fetch_flash
plug :protect_from_forgery
plug :put_secure_browser_headers
plug Auth.Auth, repo: Auth.Repo
end

pipeline :authenticated do
plug Auth.Plug.Authenticate, repo: Auth.Repo
end

pipeline :api do
plug :accepts, ["json"]
end

scope "/", Auth do
pipe_through :browser # Use the default browser stack

get "/", PageController, :index
resources "/sessions", SessionController, only: [:new, :create]
end

scope "/", Auth do
pipe_through [:browser, :authenticated]

resources "/users", UserController
resources "/sessions", S essionController, only: [:delete]
resources "/projects", ProjectController
end
end

I create an identity for user resources Verify, it works fine, but now I want to use the authenticate function in user_controller.ex to project_controller.ex.

If I copy the private function from user_controller to project_controller, instead of authentication works Project resources, but I don’t want to replicate this authentication function in every controller. I need to know what is the best way to do this code. I think routers are a good place to add authentication plugins, but I need to know where I should be Add code.

router.ex

defmodule Auth.Router do
use Auth.Web, :router

pipeline :browser do
plug :accepts, ["html"]
plug :fetch_session
plug :fetch_flash
plug :protect_from_forgery
plug :put_secure_browser_headers
plug Auth.Auth, repo: Auth.Repo
end

pipeline :api do
plug :accepts, ["json"]
end
< br /> scope "/", Auth do
pipe_through :browser # Use the default browser stack

get "/", PageController, :index
resources "/users", UserController
resources "/sessions", SessionContr oller, only: [:new, :create, :delete]
resources "/projects", ProjectController
end
end

user_controller.ex

< p>

defmodule Auth.UserController do
use Auth.Web, :controller

plug :authenticate when action in [:index, :show]

alias Auth.User

plug :scrub_params, "user" when action in [:create, :update]

def index(conn, _params) do< br /> users = Repo.all(User)
render(conn, "index.html", users: users)
end

def new(conn, _params) do
changeset = User.changeset(%User{})
render(conn, "new.html", changeset: changeset)
end

def create(conn , %{"user" => user_params}) do
changeset = User.registration_changeset(%User{}, user_params)

case Repo.insert(changeset) do
{ :ok, user} ->
conn
|> Auth.Auth.login(user)
|> put_flash(:info, "#{user.username} created successfully. ")
|> redirect(to: user_path(conn, :index))
{:error, changeset} ->
render(conn, "new.html", changeset: changeset)
end
end

def show(conn, %{"id" => id}) do
user = Repo.get!(User, id)< br /> render(conn, "show.html", user: user)
end

def edit(conn, %{"id" => id}) do
user = Repo.get!(User, id)
changeset = User.changeset(user)
render(conn, "edit.html", user: user, changeset: changeset)
end

def update(conn, %{"id" => id, "user" => user_params}) do
user = Repo.get!(User, id)
changeset = User.changeset(user, user_params)

case Repo.update(changeset) do
{:ok, user} ->
conn
|> put_flash (:info, "User updated successfully.")
|> redirect(to: user_path(conn, :show, user))
{:error, changeset} ->
render(conn , "edit.html", user: user, changeset: changeset)< br /> end
end

def delete(conn, %{"id" => id}) do
user = Repo.get!(User, id)
Repo.delete!(user)
conn
|> put_flash(:info, "User deleted successfully.")
|> redirect(to: user_path(conn, :index))
end

defp authenticate(conn, _opts) do
if conn.assigns.current_user do
conn
else
conn
|> put_flash(:error, "You must be login to access that page.")
|> redirect(to: page_path(conn, :index))
|> halt()
end
end
end

Model/user.ex

defmodule Auth.User do
use Auth. Web, :model

schema "users" do
field :username, :string
field :password_hash, :string
field :password, :string, virtual: true

timestamps
end

def changeset(model, params \ :empty) do
model
|> cast(params, ~w(username), [])
|> validate_len gth(:username, min: 3, max: 20)

end

def registration_changeset(model, params) do
model
|> changeset (params)
|> cast(params, ~w(password), [])
|> validate_length(:password, min: 6, max: 100)
|> put_pass_hash()
end

defp put_pass_hash(changeset) do
case changeset do
%Ecto.Changeset{valid?: true, changes: %{password: pass}}- >
put_change(changeset, :password_hash, Comeonin.Bcrypt.hashpwsalt(pass))
_ ->
changeset
end
end
end

Controller/auth.ex

defmodule Auth.Auth do
import Plug.Conn
import Comeonin.Bcrypt, only: [ checkpw: 2]

def init(opts) do
Keyword.fetch!(opts, :repo)
end

def call(conn, repo) do
user_id = get_session(conn, :user_id)
user = user_id && repo.get(Auth.User, user_id)
assign(conn, :current_user, user)
end

def login(conn, user) do
conn
|> assign(:current_user, user)
|> put_session(:user_id, user.id)
|> configure_session(renew: true)
end

def login_by_username_and_pass(conn, username, given_pass, opts) do
repo = Keyword.fetch!(opts, :repo)
user = repo.get_by(Auth.User, username: username)
cond do
user && checkpw(given_pass, user.password_hash) ->
{:ok , login(conn, user)}
user ->
{:error, :unauthorized, conn}
true ->
{:error, :not_found, conn}
end
end

def logout(conn) do
# configure_session(conn, drop: true)
delete_session(conn, :user_id)
end

end

Controller/ project_controller.ex

defmodule Auth.ProjectController do
use Auth.Web , :controller

plug :authenticate when action in [:index, :new, :show]

alias Aut h.Project

plug :scrub_params, "project" when action in [:create, :update]

def index(conn, _params) do
projects = Repo.all(Project)
render(conn, "index.html", projects: projects)
end

def new(conn, _params) do
changeset = Project.changeset(%Project{})
render(conn, "new.html", changeset: changeset)
end

def create(conn, %{"project "=> project_params}) do
changeset = Project.changeset(%Project{}, project_params)

case Repo.insert(changeset) do
{:ok, _project} ->
conn
|> put_flash(:info, "Project created successfully.")
|> redirect(to: project_path(conn, :index))
{:error , changeset} ->
render(conn, "new.html", changeset: changeset)
end
end

def show(conn, %{"id "=> id}) do
project = Repo.get!(Project, id)
render(conn, "show.html", project: project)
end

def edit(conn, %{"id" => id}) do
project = Repo.get!(Project, id)
changeset = Project. changeset(project)
render(conn, "edit.html", project: project, changeset: changeset)
end

def update(conn, %{"id" = > id, "project" => project_params}) do
project = Repo.get!(Project, id)
changeset = Project.changeset(project, project_params)

case Repo.update(changeset) do
{:ok, project} ->
conn
|> put_flash(:info, "Project updated successfully.")
|> redirect( to: project_path(conn, :show, project))
{:error, changeset} ->
render(conn, "edit.html", project: project, changeset: changeset)
end
end

def delete(conn, %{"id" => id}) do
project = Repo.get!(Project, id)
< br /> # Here we use delete! (with a bang) because we expect
# it to always work (and if it does not, it will raise).< br /> Repo.delete!(project)

conn
|> put_flash(:info, "Project deleted successfully.")
|> redirect(to: project_path(conn , :index))
end


# defp authenticate(conn, _opts) do
# if conn.assigns.current_user do
# conn< br /> # else
# conn
# |> put_flash(:error, "You must be login to access that page.")
# |> redirect(to: page_path(conn, :index))
# |> halt()
# end
# end
end

This It is a fairly common mode. First you need your Authenticate plug-in:

defmodule Auth.Plug.Authenticate do
@behaviour Plug
import Plug.Conn
import Phoenix.Controller, only: [put_flash: 3, redirect: 2]

def init(opts), do: opts

def call(conn, _opts) do
if conn.assigns.current_user do
conn
else
conn
|> put_flash(:error, "You must be login to access that page.")
|> redire ct(to: Auth.Router.Helpers.page_path(conn, :index))
|> halt()
end
end
end

Then In your router, you can:

defmodule Auth.Router do
use Auth.Web, :router

pipeline :browser do
plug :accepts, ["html"]
plug :fetch_session
plug :fetch_flash
plug :protect_from_forgery
plug :put_secure_browser_headers
plug Auth.Auth , repo: Auth.Repo
end

pipeline :authenticated do
plug Auth.Plug.Authenticate, repo: Auth.Repo
end

pipeline :api do
plug :accepts, ["json"]
end

scope "/", Auth do
pipe_through :browser # Use the default browser stack

get "/", PageController, :index
resources "/sessions", SessionController, only: [:new, :create]
end
< br /> scope "/", Auth do
pipe_through [:browser, :authenticated]

resources "/users", UserController
r esources "/sessions", SessionController, only: [:delete]
resources "/projects", ProjectController
end
end

Leave a Comment

Your email address will not be published.