policy_wonk v0.2.1 PolicyWonk.Enforce View Source

This the main policy enforcement plug.

Policy Enforcement

The goal of PolicyWonk.Enforce is to evaluate one or more policies and either halt the plug stack, or allow it to continue.

In a router:

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

    pipeline :browser_session do
      plug PolicyWonk.LoadResource, :current_user
      plug PolicyWonk.Enforce, :current_user
    end

    pipeline :admin do
      plug PolicyWonk.Enforce, {:user_permission, "admin"}
    end

    . . .

In a controller:

  defmodule AdminController do
    use Phoenix.Controller

    plug PolicyWonk.Enforce, {:user_permission, "admin"}

    def index(conn, _params) do
      send_resp(conn, 200, "OK")
    end
  end

If any policy returns anything other than :ok, then the plug stack is halted and given a chance to exit gracefully.

Specifying Policies

The main parameter to the PolicyWonk.Enforce plug is either a single policy or a list of policies.

  plug PolicyWonk.Enforce, :policy_1
  plug PolicyWonk.Enforce, [:policy_1, :policy_2]

The “name” of the policy can be pretty much any type you want to pass in to your policy. It doesn’t need to be an atom, although that is very convenient to match on.

These are all valid policy specifiers:

  plug PolicyWonk.Enforce, [:policy_1, :policy_2]
  plug PolicyWonk.Enforce, {:policy_s, "a string")
  plug PolicyWonk.Enforce, %{id: "an_id", data: %{color: "blue"}}

The idea is that you create matching policy functions and rely Elixir’s function matching to select the right one.

  def policy( assigns, :policy_1 ) do
    :ok
  end

  def policy( assigns, {:policy_2, name} ) do
    IO.inspect name
    :ok
  end

  def policy( assigns, %{id: id, data: %{color: color} ) do
    IO.inspect color
    :ok
  end

Use with Guards

When the PolicyWonk.Enforce is invoked inside a Phoenix controller, you can add guards against the current action.

plug PolicyWonk.Enforce, :policy_1 when action in [:index]

Handling Policy Failures

If any policy fails, then the PolicyWonk.Enforce plug calls your policy_error function with the data returned by the policy and halts the plug stack.

This is where you transform the conn to handle the error gracefully.

Specifying the Policy Module

As discussed in the documentation for PolicyWonk.Policy, the PolicyWonk.Enforce plug will look for policies first in your controller (or router) as appropriate. Then in the policy module/s specified in the config block.

If you are using the plug outside phoenix, then just the config block is checked.

You can also specify exactly which module to look in at the time you invoke the plug.

plug PolicyWonk.Enforce, %{policies: [:policy_1], module: MyPoliciesModule}

If you do specify the module, then that is the only one PolicyWonk.Enforce will look in for policies.

Evaluating Policies Outside of the Plug

You will often want to evaluate policies outside of the plug chain. For example, to show only show UI if the user has permission to see it.

PolicyWonk.Enforce provides the authorized? API for just this purpose. It evaluates the policy and returns a simple boolean value indicating success or failure.

There are two ways to access the authorized? API. The first is to call it directly, specifying the module the policies are in.

The second, prettier, way is to call use PolicyWonk.Enforce in any modules where you implement policies. This creates a local authorized? function names the module for you.

  defmodule AdminController do
    use Phoenix.Controller
    use PolicyWonk.Enforce
    . . . 
    def policy(assigns, :is_admin) do
      . . .
    end
  end

  defmodule UserController do
    use Phoenix.Controller

    def show(conn, params) do
      if AdminController.authorized?( conn, :is_admin ) do
        . . . 
      else
        . . .
     end
   end
 end

Both forms of authorized? simulate the policy finding found in the plug.

Link to this section Summary

Functions

Evaluate a policy outside of the plug stack. Returns a simple true/false boolean indicating if the policy succeeded or failed. Your policy_error function is not called in the event of a failure

Call is used by the plug stack

Initialize an invocation of the plug

Link to this section Functions

Link to this function authorized?(module, conn, policies) View Source
authorized?(atom(), any(), List.t() | any()) :: boolean()

Evaluate a policy outside of the plug stack. Returns a simple true/false boolean indicating if the policy succeeded or failed. Your policy_error function is not called in the event of a failure.

Parameters

  • module A module to look for policies in. If nil, on the config policies will be used.
  • data Resource data to passed into your policy. If you pass a conn in, then the assigns field be extracted and sent to your policy.
  • policies A list of policies to be evaluated. Can also be a single policy.

Call is used by the plug stack.