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
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 pre>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
endController/ 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
endThis 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
endThen 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