pundit v1.0.0 Pundit View Source

Pundit provides a set of helpers which guide you in leveraging regular Elixir methods to build a simple authorization system. This library is based heavily on Jonas Nicklas' Ruby project of the same name.

Simple Elixir functions are defined for a given struct and allow you to encapsulate authentication logic. You can use this code within a module that is an Ecto.Schema, but that's not necessary. The action names are taken from the list of actions defined by Phoenix controllers.

Examples

Here's a basic example, starting with a simple struct for a Post. A module named Post.Policy should be created to encapsulate all of the access methods (Pundit will automatically look for the <struct module>.Policy module to determine the module name to look at for access methods).

To declare an initial set of access functions (show?, edit?, delete?, etc) which all return false (default safe!), just usePundit.DefaultPolicy. You can then override the functions as needed with the logic necessary to determine whether a user should be able to perform the given action. In this example, we only determine whether a user can edit? a post, leaving all other functions (like delete?) to return the default of false.

defmodule Post do
  defstruct [:author, :title, :body, :comments]

  defmodule Policy do
    # This will initialize all the functions listed below, that all return false
    # by default. Override them individually to return true when they should.
    use Pundit.DefaultPolicy

    def edit?(post, user) do
      user.name == post.author
    end
  end
end

post = %Post{author: "Snake Plissken"}
author = %{name: "Snake Plissken"}
# next line is same as Pundit.can?(post, author, :edit?)
if Pundit.edit?(post, author) do
  IO.puts("Can edit!")
end

if Pundit.delete?(post, author) do
  IO.puts("This line should never be called")
end

# raise exception if user should be able to do a thing
Pundit.authorize!(post, author, :edit?)

Scope

You can also provide query scope for a struct (say, if you're using Ecto.Schema) for a given user. For instance, say our Post was an Ecto schema. Our function for scoping all Posts to a specific User could be to find all Posts that were authored by a user. For instance:

defmodule Post do
  use Ecto.Schema
  import Ecto.Query, only: [from: 2]

  defmodule Policy do
    use Pundit.DefaultPolicy

    def scope(query, user) do
      from post in query,
        where: post.author_id == ^user.id
    end
  end
end

user = MyApp.Repo.get(User, 1)
posts = Pundit.scope(Post, user) |> Repo.all()

query = from p in Post, where: p.comment_count > 10
popular_posts = Pundit.scope(query, user) |> Repo.all()

Link to this section Summary

Functions

Return a tuple based on whether a user can perform the action on the thing.

Raise a Pundit.NotAuthorizedError exception unless the user can perform the action on the thing.

Determine if a use can perform an action on a given thing.

Returns true only if the user should be allowed to create a new kind of thing.

Returns true only if the user should be allowed to delete a thing.

Returns true only if the user should be allowed to see a form for updating the thing.

Returns true only if the user should be allowed to see an index (list) of the given things.

Returns true only if the user should be allowed to see a form to create a new thing.

Scope a Ecto.Query or Ecto.Schema to a given user.

Returns true only if the user should be allowed to see the given thing.

Returns true only if the user should be allowed to update the attributes of a thing.

Link to this section Functions

Link to this function

authorize(thing, user, action)

View Source
authorize(thing :: struct() | module(), user :: term(), action :: atom()) ::
  {:ok} | {:error, String.t()}

Return a tuple based on whether a user can perform the action on the thing.

Returns {:ok} if a user can perform the action, or {:error, message} if not.

Link to this function

authorize!(thing, user, action)

View Source
authorize!(thing :: struct() | module(), user :: term(), action :: atom()) ::
  boolean()

Raise a Pundit.NotAuthorizedError exception unless the user can perform the action on the thing.

Link to this function

can?(thing, user, action)

View Source
can?(thing :: struct() | module(), user :: term(), action :: atom()) ::
  boolean()

Determine if a use can perform an action on a given thing.

This will attempt to call a function with the same name as the action on the policy module of the given thing. For instance:

Pundit.can?(post, user, :edit?)

is the same as:

Post.Policy.edit?(post, user)
Link to this function

create?(thing, user)

View Source
create?(thing :: struct() | module(), user :: term()) :: boolean()

Returns true only if the user should be allowed to create a new kind of thing.

Link to this function

delete?(thing, user)

View Source
delete?(thing :: struct() | module(), user :: term()) :: boolean()

Returns true only if the user should be allowed to delete a thing.

Link to this function

edit?(thing, user)

View Source
edit?(thing :: struct() | module(), user :: term()) :: boolean()

Returns true only if the user should be allowed to see a form for updating the thing.

See the page on Phoenix controllers for more details on the purpose of this action.

Link to this function

index?(thing, user)

View Source
index?(thing :: struct() | module(), user :: term()) :: boolean()

Returns true only if the user should be allowed to see an index (list) of the given things.

Link to this function

new?(thing, user)

View Source
new?(thing :: struct() | module(), user :: term()) :: boolean()

Returns true only if the user should be allowed to see a form to create a new thing.

See the page on Phoenix controllers for more details on the purpose of this action.

Link to this function

scope(query, user)

View Source
scope(schema :: module() | Ecto.Query.t(), user :: term()) :: Ecto.Query.t()

Scope a Ecto.Query or Ecto.Schema to a given user.

This will call the function scope on the policy module of the given thing. For instance:

Pundit.scope(Post, user)

is the same as:

Post.Policy.scope(Post, user)

Here's an example with a Ecto.Query:

query = from post in Post, where: post.comments > 10
# This call...
Pundit.scope(query, user)
# Is the same as...
Post.Policy.scope(query, user)

This is just helpful shorthand.

Link to this function

show?(thing, user)

View Source
show?(thing :: struct() | module(), user :: term()) :: boolean()

Returns true only if the user should be allowed to see the given thing.

Link to this function

update?(thing, user)

View Source
update?(thing :: struct() | module(), user :: term()) :: boolean()

Returns true only if the user should be allowed to update the attributes of a thing.