Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to restrict access to certain routes in Phoenix?

I have a small Phoenix application allowing users to login and consult their profile. I used the following simple route:

resources "/users", MyApp.UserController

But this allows every user to see the list of users via the :index action, as well as delete or update any user.

What is the easiest way to restrict access to admins only? Should I add a check in front of every action? Or should I create a "/admin" resource which would handle those operations? What is the recommended way?

like image 748
adanselm Avatar asked Sep 26 '14 08:09

adanselm


2 Answers

You would use a plug in the UserController. 0.4.x has no ability for conditionally plug s, but you could achieve what you want with something like:

defmodule MyApp.UserController do
  use Phoenix.Controller

  plug :authenticate, :admin
  plug :action

  def index(conn, _) do
    render conn, "index"
  end

  def create(conn, params) do
    # do the creating
  end
  ...

  defp authenticate(conn, :admin) do
    do_auth(conn, action_name(conn))
  end
  defp do_auth(conn, action) when action in [:create, :update, :destroy] do
    if AdminAuth.authenticated?(conn) do
      conn
    else
      halt conn
    end
  end
  defp do_auth(conn, _action), do: conn
end

The changes coming soon in 0.5 will allow easier conditional plugs, i.e.:

defmodule MyApp.UserController do
  use Phoenix.Controller

  plug :authenticate, :admin when action in [:create, :update, :destroy]

  def index(conn, _) do
    render conn, "index"
  end

  def create(conn, params) do
    # do the creating
  end
  ...

  defp authenticate(conn, :admin) do
    if AdminAuth.authenticated?(conn) do
      conn
    else
      halt conn
    end
  end
end

It's a good idea to keep your controllers for public/restricted access separate, so I would add an Admin.UserController like you made reference to for the restricted functionality.

like image 83
Chris McCord Avatar answered Nov 13 '22 23:11

Chris McCord


You could also define a separate pipeline for the authenticated endpoints:

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

  pipeline :admin do 
    plug :accepts, ["html"]

    plug Authentication # this represents some plug that provides authentication
  end

  scope "/", MyApp do
    pipe_through :browser

    resources "/things", ThingController
  end

  scope "/admin", MyApp do
    pipe_through :admin

    resources "/admin/things", Admin.ThingsController
  end
end

The admin scope is just an example, it can be anything you like, but the pipeline idea remains consistent.

This technique will keep your controllers cleaner, but is not always possible. It depends on your exact requirements.

like image 27
Jason Harrelson Avatar answered Nov 13 '22 23:11

Jason Harrelson