Policy Wonk - The Tutorial

Policy Wonk is a lightweight authorization library for any Phoenix or Plug application. It improves your security and follows the Elixir way of doing things. Check it out on Hex or Github. It’s cool.

Update on v1.0 — January 16, 2018

Update: I just posted v1.0.0-rc.0 to hex. I’ll let it sit for a while for comments, before making it official. You can get the rc on hex.

PolicyWonk is almost completely re-written for version 1.0. After living with it for well over a year, I realized there were a set of issues that warranted re-opening the underlying architecture.

  • It wasn’t compatible with Phoenix 1.3 umbrella apps. Or rather, you couldn’t have separate policies for different apps in an umbrella.
  • It had a whole mess of complexity that simply wasn’t needed. I never used
    most of the “shortcut” options since the more explicit versions (with slightly more typing) were always clearer.
  • Returning errors from Policies was too vague. I want to know errors are being processed!
  • The config file data isn’t necessary in a more explicit model.
  • Naming was inconsistent between policies and resources.

Version 1.0 takes these observations (and more), fixes them, and simplifies the configuration dramatically. It has less code and is overall simpler and faster.

There is a little work to upgrade from a older versions, but the overall shape of your code will stay the same, so the work is small and well worth it.

From here down, this post is about the original version…

In a Nutshell

Authorization (Auth-Z) is the process of deciding what a user/entity is allowed to do after they’ve been authenticated.

Policy Wonk encourages you to create small “policy” functions that return a yes/no decision about whether or not a user (or other resource) has permissions to use your Phoenix action.

These policies are evaluated in the “plug” stack, so you can make these decisions before your action is called. This keeps your policy decisions isolated from your business logic, and also keeps them composable.

The basic steps to use Policy Wonk are:

  1. Install Policy Wonk in your application .
  2. Write some policy functions.
  3. Use the PolicyWonk.Enforce plug to evaluate your policies.

You can enforce policies from your router (protecting whole swaths of your app with one line of code), or use them in specific controllers.

Plugs

Policy Wonk provides three main plugs.

Decisions are made before controller actions are called, isolating authorization logic, encouraging policy reuse, and reducing the odds of messing Auth-Z up as you develop your controllers.

Part 1: Getting Started

To get started, we are going to install Policy Wonk and get some policies running. First, lets run them from the router, so that you can protect multiple actions with one plug call.

Step 1.1 — Installation

To install Policy Wonk in your Elixir/Phoenix application, add a reference to it in the deps portion of your applications mix.exs file.

defp deps do
[
# ...
{:policy_wonk, "~> 0.2"}
# ...
]
end

Don’t forget to run mix deps.get.

You can get to the various Policy Wonk locations here

Step 1.2 — Create a Policy

For your first policy, let's make sure a user is logged in to your app.

I keep most of my policies in the file lib/policy_wonk/policies.ex. Create this file now, and fill it in with the code below. Change MyApp into something relevant to your app.

Note: I am assuming you already have a user model. You will need to change %MyApp.User{} into something relevant to your app

This function is a policy…

def policy( assigns, :current_user ) do
case assigns[:current_user] do
_user = %MyApp.User{} -> :ok
_ -> :current_user
end
end

The idea is that you examine the contents of the assigns map in the current conn and decide whether or not to allow the plug chain to continue.

If you want the policy to succeed, return the atom :ok. This is the only way to indicate the policy passed.

If you want the policy to fail, return anything else.

The first parameter PolicyWonk.Enforce passes to your policy function, it passes in the ‘assigns’ map from the current conn.

The second Parameter is whatever you want to use to identify the policy and pass in values. If you aren’t familiar with Elixir’s function pattern matching, you should spend some time with the books Programming Elixir 1.3 and Elixir in Action. Both of which are excellent.

Think of :current_user as the name of the policy. This could also be a tuple such as {:permission, “admin”} or a map or whatever you want.

Step 1.3 — Error Handling

The policy_error function at the bottom of the policies.ex file is called when one of your policies returns anything other than :ok.

Unlike a policy, you can directly manipulate the conn passed into your policy_error function. You can redirect it to another action, put flash messages render views, or whatever else you might want to do.

The second parameter to your policy_error function is the non-ok value you returned from your policy. You can match on this parameter, or just use it as input.

I like collect my basic action error handlers (unauthorized, not-found, etc.) in a common file located at web/error_handlers.ex. That way I can use these error handlers with both Policy Wonk and Guardian and others…

This is the MyApp.ErrorHandlers module called from the code above. Feel free to do something else if you want.

Finally, the point of enforcing policies is to stop the plug chain before it gets to your action. The PolicyWonk.Enforce plug will automatically call Plug.Conn.halt() in the event of policy failure. Calling halt() in your error handlers is good if they can be called from multiple locations, but is redundant with Policy Wonk.

Step 1.4 — Configure the Policy Module

Add the following configuration block to your config/config.exs file.

config :policy_wonk, PolicyWonk,
policies: MyApp.Policies

This tells Policy Wonk to look in the MyApp.Policies module for policies that aren’t found on the current controller or router.

Step 1.5 — Use the Policy!

Add a call to the PolicyWonk.Enforce to a pipeline in your router like this:

defmodule Loom.Router do
use Loom.Web, :router
  pipeline :browser_session do
plug PolicyWonk.Enforce, :current_user
end
  . . .
end

Now, when any action comes in to your Phoenix application that uses the :browser_session pipeline, your :current_user policy will be used by PolicyWonk.Enforce to decide if the action should be called or not.

Try it out.

If you are setting conn.assigns.current_user somewhere in your plug stack (could be the guardian plug or something using comeonin), then the policy will pass and your action will run.

If conn.assigns.current_user is not set to a user, the the policy will fail and… You should see whatever action your error handler chose to take.

Step 1.6 —Example: Check User Permissions

Checking that a user has certain permissions beyond simply being signed in is a policy that I check frequently, across many controllers. For example, is the user an admin? If so, are they also a user admin? Content? Invitations? Can they edit other users permissions?

This the permissions policy I use in my apps. It is also my most complicated policy. Feel free to write your own, but it is helpful to understand this one, since I use it a lot.

Put this policy in your lib/policy_wonk/policies.ex file. You should not have a single blanked “admin” permission in your app. I’ve seen that abused before. Instead, give different people just the level of access they need.

A few assumptions: This code assumes that your User model has a field called :permissions, which is an array of strings. Each string represents a single permission.

For the love of sanity, encrypt permissions in the db. Check out Cloak.

Because I wanted to be able to check multiple permissions with a single plug call, the perms part of the incoming tuple can be either a single string or a list of strings. You could call it like this…

# one permission
plug PolicyWonk.Enforce, {:user_perm, "admin"}
# multiple permissions
plug PolicyWonk.Enforce, {:user_perm, ["admin", "admin_user"]}

First, the policy enforces that a current_user has been loaded. If not, it fails with the error_data of :current_user. See loading resources in part 2.

Once you have the current_user, it looks for the permissions field. If it is nil, then obviously the user doesn’t have the requested permissions.

case user.permissions do
nil -> {:user_perm, perms} # Fail. No permissions

If the user does have permissions data, then we need to make sure they have all the strings requested in the incoming perms list.

user_perms ->
Enum.all?( perms, &(Enum.member?(user_perms, to_string(&1))) )
|> case do
true -> :ok # Success.
false -> {:user_perm, perms} # Fail. Permission missing
end

Enum.All? scans the incoming list of permissions. Enum.Member? checks that a single permission is contained in the user’s permissions field.

If all the requested permissions are present in the user’s permissions field, then the policy returns :ok and the plug stack can proceed.

If any requested permission is missing, then the policy returns the incoming permissions request as the outgoing error data. In that way you could write a specific error handler to do something such as logging the failed permissions.

The final bit makes it so you can pass a single permission in without putting it into an array. Notice that all it does is put the incoming non-list value into a list then pass it back into itself.

def policy( assigns, {:user_perm, one_perm} ), do:
policy( assigns, {:user_perm, [one_perm]} )

Examples using the permissions policy

Called in a router…

defmodule MyApp.Router do
use MyApp.Web, :router
  pipeline :admin do
plug PolicyWonk.Enforce, {:user_perm, "admin"}
end
  . . .

Called in a controller…

defmodule Admin.UserController do
use MyApp.Web, :controller
  plug PolicyWonk.Enforce, {:user_perm, ["admin_user", "admin_inv"]}
 . . .

Optional — Guards

In a Phoenix application, you can use guards with your plugs.

This is a marvelous piece of Elixir trickery that took me a bit to wrap my head around. Check out the Phoenix source if you want to dive deep in what is going on.

This means you can use guards to limit any policy enforcement to just the actions you want.

defmodule MyApp.AdminController do
use Phoenix.Controller
  plug PolicyWonk.Enforce, :current_user when action in [:index]
  . . .

I cannot emphasize enough how cool and powerful this is. I experimented with all sorts of ways of calling and organizing policies. Using Elixir matching to find a policy and action guards to choose when to run them caused all the complexity to simply fall away.


Part 2: Loading Resources

In order to evaluate policies, resources need to be loaded into memory first.

In a plug stack, code is run before your controller’s actions, so you need to use some other plug to load resources into the conn.assigns field before running the PolicyWonk.Enforce plug.

PolicyWonk.LoadResource does this job. You create simple load_resource functions that load and return a single resource. If are already loading resources with other plugs (Guardian), then that works too.

Like policies, these load_resource functions are reusable, composable, and located in controllers, your router, or a shared module.

Step 2.1: Create the Loader Module

I keep my shared policies in the file lib/policy_wonk/loaders.ex. Create this file now, and fill it in with the following code. Change MyApp into something relevant to your app.

This creates a load_resource function that simply adds a known string into the assigns map. It could easily load a record from a database, pull data from an ETS table, or simply assign a constant.

Your load_resource function indicates success by returning a three part tuple in the form of {:ok, :name, resource}.

The :ok indicates success. The atom :name is the key the resource will be assigned to in conn.assigns. And the last part is the resource itself. Any other response indicates a loading failure.

The :simple loader above will add the string “A simple string resource” to the conn’s assigns map.

Step 2.2 — Configure the Loader Module

Modify your :policy_wonk configuration block to use your new loaders file.

config :policy_wonk, PolicyWonk,
policies: MyApp.Policies,
loaders: MyApp.Loaders,
load_async: true

This tells Policy Wonk to look in the MyApp.Loaders module for load_resource functions that aren’t found on the current controller or router.

The option load_async indicates if you want to load resources in a single call to PolicyWonk.LoadResource asynchronously or not. See options below.

Step 2.3 — Use the new loader

In your router, load the resource in the :browser_session pipeline.

defmodule Loom.Router do
use Loom.Web, :router
  pipeline :admin do
plug PolicyWonk.LoadResource, :simple
plug PolicyWonk.Enforce, {:user_perm, "admin"}
end
. . .

By placing it before the call to PolicyWonk.Enforce, you ensure that the :simple resource is assigned to conn.assigns before your policy runs.

You can also call loaders from controllers.

defmodule Admin.UserController do
use Phoenix.Controller
  plug PolicyWonk.LoadResource, :simple  
plug PolicyWonk.Enforce, {:user_perm, "admin_user"}
  . . .

Step 2.4 — Loader Error Handling

Like policies, you need to provide a function to call in the event of an error loading a resource.

The load_error function at the bottom of the loaders.ex file is called when one of your policies returns anything other than :ok. This is where you directly manipulate the conn to handle the error state as appropriate.

The second parameter to your policy_error function is the non-ok value returned from your policy. You can match on this parameter, or just use it as input.

As you can see from the sample in 2.1 above, I am again using my MyApp.ErrorHandlers module to manipulate the conn.

Unlike policies, you may or may not want to halt the plug chain on a load error. PolicyWonk.LoadResource does NOT call Plug.Conn.halt() for you.

Step 2.5 — Example: Load Current User

No matter how you authenticate your current user, you will often need to reference it from your policies.

This the :current_user resource loader I use in my apps. It is the most complicated loader I use. Feel free to write your own, but it is helpful to understand this one, since I use it a lot.

First, it checks the conn’s assigns map to see if it is already loaded.

if conn.assgins[:current_user] do
# current_user is already loaded
{:ok, :current_user, conn.assigns[:current_user]}

A common technique in testing is to directly assign the current user to the test conn before calling get/post/put/delete on an action. This line allows that shortcut to work

Next, I get the id of the current user from the conn’s session.

case Plug.Conn.get_session(conn, :current_user_id) do
# no user is signed in, assign nil
nil -> {:ok, :current_user, nil}
user_id ->

In my apps, I use comeonin to authenticate the user, then I stash the id of the user in the session. Actually, I stash an encrypted token containing the user’s id in the session. That is an exercise for the reader. Check out Phoenix.Token.sign and Phoenix.Token.verify to get started.

If no user id is in the session, I return success, but assign nil. Enforcing that a user is required is a job for a policy, not the loader. There are many cases where it is ok for there to be no user signed in. For example: the sign-in page.

Lastly, if we did get a signed-in user id, then load the record from the database and return success. If it doesn’t load from the db, then I want an error to be handled.

case Repo.get(MyApp.User, user_id) do
nil ->
# the user isn't in the db - fail
"User not found"
user ->
# success. loaded user from db
{:ok, :current_user, user}
end

Optional — Synchronous vs. Asynchronous Loading

One of my favorite parts of working with Elixir is the ease of writing asynchronous code.

Loading a resource from a database, generating hashes, or other operations can often take a measurable amount of time to complete, even though they are not necessarily compute intensive.

PolicyWonk.LoadResource helps by (optionally) loading the resources you specify in any given call asynchronously.

plug PolicyWonk.LoadResource, [:thing_a, :thing_b]

In this case, both :thing_a and :thing_b are going to hit the database. If the :policy_wonk config block has set load_async to true, then they will be loaded in parallel, saving significant time.

You can also request asynchronous loading with the expanded form of the plug invocation.

plug PolicyWonk.LoadResource, %{resources: [:thing_a, :thing_b], async: true}

If you are curious, asynchronous loading is why you don’t assign the resource directly into the conn yourself. By returning the resource in a tuple such as {:ok, :name, resource}, PolicyWonk.LoadResource is able to load multiple resources at the same time without confusing what is assigned when.

Optional— Guards

Like policies you can use guards with calls to PolicyWonk.LoadResource.


Part 3: Local Policies and Loaders

As projects grow bigger, so does the need to keep things organized. This is true for all your code, but especially so for anything to do security.

The lib/policy_wonk/policies.ex and lib/policy_wonk/loaders.ex files we created above are great places to put policies and loaders that you are going to use in multiple locations.

On the other hand, if you have a policy or loader that you want to use in a single Controller, then it should live in that Controller module. Putting many one-off policies in a single module is asking for code confusion, especially since you lose the context of how that policy is used.

Policy Wonk is Phoenix aware, so whenever you call PolicyWonk.Enforce from either a Phoenix Controller or Router, it will look for policies and loaders in that module first, then in the module/s you specified in the configuration block.

I keep my per-controller policies and loaders at the bottom of the Controller module they are called from. Then I always know where to look for them and the configured policy module stays organized with just the ones that are truly shared.

Overriding Policies and Loaders

Even if a policy or loader is declared in the configured module, you can still declare another version of it in a controller (or router). Policy Wonk will preferentially use the version in that controller.

This allows you to “override” a shared policy with a specific one-off version. You can do this with policy_error, load_resource, and load_error handlers as well.


Part 4: Using Policies Outside the Plug Stack

The final piece of the puzzle is using policies outside the plug stack.

For example, it would be nice to use a policy in a template to only show a link to your administration dashboard if the current user has admin privileges.

Adding use PolicyWonk.Enforce to any module that defines policies will generate an authorized? function on that module, which you can use outside the plug chain. This function evaluates a given policy and returns a simple true/false boolean indicating pass for fail. It does not call you policy_error function if the policy fails.

You can see the call to use PolicyWonk.Enforce at the top of the policy sample from part 1.

Even if you are only using shared policies from the configuration block, you probably want to add use PolicyWonk.Enforce to your controllers. In that way, you can always call SomeController.authorized? and know you are calling it from the correct context.

defmodule AdminController do
use Phoenix.Controller
use PolicyWonk.Enforce
  plug PolicyWonk.Enforce, {:user_perm, "admin"}
. . .

I’ve gotten to the point where I’ve added use PolicyWonk.Enforce to the controller section of my web.ex file.

Calling authorized? does not activate the plug stack. It evaluates a single policy against the current conn, so you will need to know which policy to call.

Using Authorized? in a Template

Let’s say you have an administration dashboard in your app. You only want to show a link to it on your homepage to users who have the “admin” permission.

Note: I like to use haml, which you can get through the phoenix_haml hex package.

- if AdminController.authorized?(@conn, {:user_perm, "admin"}) do
= render "Admin Dashboard", admin_path(@conn, :index)

You could also used the configured policy module directly

- if MyApp.Policies.authorized?(@conn, {:user_perm, "admin"}) do
= render "Admin Dashboard", admin_path(@conn, :index)

If the first parameter you pass in is the current conn, then authorized? knows to pass conn.assigns to your policy. If you pass in anything else, then that value is passed directly to your policy function.

Behind the scenes, the generated authorized? in your module is a simple wrapper that is really calling PolicyWonk.Enforce.authorized?.

I call authorized? from templates, controllers, and even channels. This keeps the policy decision making centralized and consistent throughout the app.


Conclusion

Like a good game, I’m finding that Policy Wonk is easy learn, but deep enough to take a while to really explore. And I wrote the darned thing!

In my projects Policy Wonk is delivering on all the goals I set out for it.

  • My authorization code is isolated into policies that I can easily re-use.
  • Authorization code is no longer intermixed with business logic.
  • Whole swaths of actions are protected in the router.
  • It is much more readable and maintainable.

You can read the full documentation here.

I hope Policy Wonk serves you as well as it is serving me.


If this was helpful, check out my thoughts on integration testing…

Integration Testing Phoenix Applications