permission_ex v0.1.0 PermissionEx

Main module and interface to testing permissions.

If you wish to test a single permission for equality, then you would use PermissionEx.test_permission/2.

If you wish to test an entire permission struct for matching allowed permissions then you would use PermissionEx.test_permissions/2.

If you wish to test an entire permission struct for matching allowed permissions on a struct tagged map then you would use PermissionEx.test_tagged_permissions/2.

The examples in this module use these definitions of structs for testing permissions:


defmodule PermissionEx.Test.Structs.User do
  @moduledoc false
  @derive [Poison.Encoder]
  defstruct name: nil
end

defmodule PermissionEx.Test.Structs.Page do
  @moduledoc false
  @derive [Poison.Encoder]
  defstruct action: nil
end

defmodule PermissionEx.Test.Structs.PageReq do
  @moduledoc false
  @derive [Poison.Encoder]
  defstruct action: nil
end

defmodule PermissionEx.Test.Structs.PagePerm do
  @moduledoc false
  @derive [Poison.Encoder]
  defstruct action: nil
end

Summary

Types

A Permission matcher is either anything, of which it must then match the required permission precisely, or it is a tuple of {:any, [permission]} where each item in the list will be tested against the requirement as a base permission, if any are true then this matches

This is a set of permissions such as %{}, [%{}], etc

This is a map that has a mapping of a %{Struct => %Struct{}}

Functions

This tests a specific required permission against a specific permission

This tests a specific requirement against a set of permission

This takes a map of permissions with the keys being a tag to be looked up on. The required permission struct type is the tag to match on

Types

permission :: {:any, [permission]} | any

A Permission matcher is either anything, of which it must then match the required permission precisely, or it is a tuple of {:any, [permission]} where each item in the list will be tested against the requirement as a base permission, if any are true then this matches.

permissions ::
  :_ |
  boolean |
  nil |
  [permissions] |
  %{any => permission} |
  struct

This is a set of permissions such as %{}, [%{}], etc…

A :_ or true matches any entire requirement set.

A false or nil will always not match.

A list of permissions will each be checked individually against the requirement, if any are a true match then it returns true.

A struct or map will be tested against the requirements directly, a struct is treated like a map except the :__struct__ field will not be tested, useful if you want a requirement and permission to use different structs. Do note that the tagged_permissions keys should match the :__struct__ of the requirement struct, not of the permission struct. I.E. given a PermissionEx.Test.Structs.PageReq and a PermissionEx.Test.Structs.PagePerm, such as if you want the default values for the requirement stuct to be by default tight or lenient, and the opposite for the permission struct, then calling PermissionEx.test_permissions/2 will be like:


iex> PermissionEx.test_tagged_permissions(%PermissionEx.Test.Structs.PageReq{action: :show}, %{PermissionEx.Test.Structs.PageReq => [%PermissionEx.Test.Structs.PagePerm{action: :_}]})
true

iex> PermissionEx.test_tagged_permissions(%PermissionEx.Test.Structs.PageReq{action: :show}, %{PermissionEx.Test.Structs.PagePerm => [%PermissionEx.Test.Structs.PagePerm{action: :_}]})
false
tagged_permissions ::
  struct |
  %{:admin => tagged_permissions, atom => permissions} |
  %{atom => permissions}

This is a map that has a mapping of a %{Struct => %Struct{}}.

The value being an actual struct is optional, it can also be a map on its own, as long as it has matching keys => values of the struct, any missing will have the requirement be false unless the requirement is :_.

If there is an :admin key in the permissions, then this is checked first and can act as an easy override for the main permission set.

Functions

test_permission(required, permission)

Specs

test_permission(any, permission) :: boolean

This tests a specific required permission against a specific permission.

  • If either are :_ then it is true.
  • If both are identical, then it is true.
  • If the permission is the tuple {:any, [<permissions>]} then each permission in the list is tested against the requirement

Examples


  iex> PermissionEx.test_permission(:_, :_)
  true

  iex> PermissionEx.test_permission(:_, nil)
  true

  iex> PermissionEx.test_permission(nil, :_)
  true

  iex> PermissionEx.test_permission(nil, nil)
  true

  iex> PermissionEx.test_permission(nil, :notnil)
  false

  iex> PermissionEx.test_permission(:notnil, nil)
  false

  iex> PermissionEx.test_permission(1, 1)
  true

  iex> PermissionEx.test_permission(1, 1.0)
  false

  iex> PermissionEx.test_permission('test', 'test')
  true

  iex> PermissionEx.test_permission("test", "test")
  true

  iex> PermissionEx.test_permission('test', "test")
  false

  iex> PermissionEx.test_permission(:show, :show)
  true

  iex> PermissionEx.test_permission(:show, :edit)
  false

  iex> PermissionEx.test_permission(:show, {:any, []})
  false

  iex> PermissionEx.test_permission(:show, {:any, [:show]})
  true

  iex> PermissionEx.test_permission(:show, {:any, [:show, :edit]})
  true

  iex> PermissionEx.test_permission(:show, {:any, [:edit, :show]})
  true

  iex> PermissionEx.test_permission(:show, {:any, [:edit, :otherwise]})
  false
test_permissions(required, permissions)

Specs

test_permissions(struct, permissions) :: boolean

This tests a specific requirement against a set of permission.

See PermissionEx.test_permission/2 for possible permission formats.

Examples


  iex> PermissionEx.test_permissions(%PermissionEx.Test.Structs.Page{action: :show}, :_)
  true

  iex> PermissionEx.test_permissions(%PermissionEx.Test.Structs.Page{action: :show}, true)
  true

  iex> PermissionEx.test_permissions(%PermissionEx.Test.Structs.Page{action: :show}, false)
  false

  iex> PermissionEx.test_permissions(%PermissionEx.Test.Structs.Page{action: :show}, nil)
  false

  iex> PermissionEx.test_permissions(%PermissionEx.Test.Structs.Page{action: :show}, [])
  false

  iex> PermissionEx.test_permissions(%PermissionEx.Test.Structs.Page{action: :show}, %{})
  false

  iex> PermissionEx.test_permissions(%PermissionEx.Test.Structs.Page{action: :show}, %{action: :edit})
  false

  iex> PermissionEx.test_permissions(%PermissionEx.Test.Structs.Page{action: :show}, %{action: :show})
  true

  iex> PermissionEx.test_permissions(%PermissionEx.Test.Structs.Page{action: :show}, %{action: :_})
  true

  iex> PermissionEx.test_permissions(%PermissionEx.Test.Structs.Page{action: :show}, %{action: true})
  false

  iex> PermissionEx.test_permissions(%PermissionEx.Test.Structs.Page{action: :show}, %{action: {:any, [:edit, :show]}})
  true

  iex> PermissionEx.test_permissions(%PermissionEx.Test.Structs.Page{action: :show}, %PermissionEx.Test.Structs.Page{})
  false

  iex> PermissionEx.test_permissions(%PermissionEx.Test.Structs.Page{action: :show}, %PermissionEx.Test.Structs.Page{action: :edit})
  false

  iex> PermissionEx.test_permissions(%PermissionEx.Test.Structs.Page{action: :show}, %PermissionEx.Test.Structs.Page{action: :show})
  true

  iex> PermissionEx.test_permissions(%PermissionEx.Test.Structs.Page{action: :show}, %PermissionEx.Test.Structs.Page{action: :_})
  true

  iex> PermissionEx.test_permissions(%PermissionEx.Test.Structs.Page{action: :show}, %PermissionEx.Test.Structs.Page{action: true})
  false

  iex> PermissionEx.test_permissions(%PermissionEx.Test.Structs.Page{action: :show}, %PermissionEx.Test.Structs.Page{action: {:any, [:edit, :show]}})
  true

  iex> PermissionEx.test_permissions(%PermissionEx.Test.Structs.Page{action: :show}, [:_])
  true

  iex> PermissionEx.test_permissions(%PermissionEx.Test.Structs.Page{action: :show}, [true])
  true

  iex> PermissionEx.test_permissions(%PermissionEx.Test.Structs.Page{action: :show}, [false])
  false

  iex> PermissionEx.test_permissions(%PermissionEx.Test.Structs.Page{action: :show}, [nil])
  false

  iex> PermissionEx.test_permissions(%PermissionEx.Test.Structs.Page{action: :show}, [[]])
  false

  iex> PermissionEx.test_permissions(%PermissionEx.Test.Structs.Page{action: :show}, [%{}])
  false

  iex> PermissionEx.test_permissions(%PermissionEx.Test.Structs.Page{action: :show}, [%{action: :edit}])
  false

  iex> PermissionEx.test_permissions(%PermissionEx.Test.Structs.Page{action: :show}, [%{action: :show}])
  true

  iex> PermissionEx.test_permissions(%PermissionEx.Test.Structs.Page{action: :show}, [%{action: :_}])
  true

  iex> PermissionEx.test_permissions(%PermissionEx.Test.Structs.Page{action: :show}, [%{action: true}])
  false

  iex> PermissionEx.test_permissions(%PermissionEx.Test.Structs.Page{action: :show}, [%{action: {:any, [:edit, :show]}}])
  true

  iex> PermissionEx.test_permissions(%PermissionEx.Test.Structs.Page{action: :show}, [%PermissionEx.Test.Structs.Page{}])
  false

  iex> PermissionEx.test_permissions(%PermissionEx.Test.Structs.Page{action: :show}, [%PermissionEx.Test.Structs.Page{action: :edit}])
  false

  iex> PermissionEx.test_permissions(%PermissionEx.Test.Structs.Page{action: :show}, [%PermissionEx.Test.Structs.Page{action: :show}])
  true

  iex> PermissionEx.test_permissions(%PermissionEx.Test.Structs.Page{action: :show}, [%PermissionEx.Test.Structs.Page{action: :_}])
  true

  iex> PermissionEx.test_permissions(%PermissionEx.Test.Structs.Page{action: :show}, [%PermissionEx.Test.Structs.Page{action: true}])
  false

  iex> PermissionEx.test_permissions(%PermissionEx.Test.Structs.Page{action: :show}, [%PermissionEx.Test.Structs.Page{action: {:any, [:edit, :show]}}])
  true
test_tagged_permissions(required, tagged_perm_map)

Specs

test_tagged_permissions(struct, tagged_permissions) :: boolean

This takes a map of permissions with the keys being a tag to be looked up on. The required permission struct type is the tag to match on.

In the key of :admin, if true, will always return true no matter what the required matcher wants, it is an override that allows all permissions.

If the key of :admin contains a map, then the tag will be looked up in it and tested against, such as if true then they will get permission for that tag regardless.

If a given tag has the boolean of ‘true’ then it will always return true, basically giving admin just for that specific tag.

See PermissionEx.test_permissions/2 for how a permission struct/map is matched.

See PermissionEx.test_permission/2 for possible permission formats.

Examples


  iex> PermissionEx.test_tagged_permissions(%PermissionEx.Test.Structs.Page{}, %{admin: true})
  true

  iex> PermissionEx.test_tagged_permissions(%PermissionEx.Test.Structs.Page{}, %{admin: %{PermissionEx.Test.Structs.Page => true}})
  true

  iex> PermissionEx.test_tagged_permissions(%PermissionEx.Test.Structs.User{}, %{admin: %{PermissionEx.Test.Structs.Page => true}})
  false

  iex> PermissionEx.test_tagged_permissions(%PermissionEx.Test.Structs.Page{action: :show}, %{admin: %{PermissionEx.Test.Structs.Page => %{action: :show}}})
  true

  iex> PermissionEx.test_tagged_permissions(%PermissionEx.Test.Structs.Page{action: :edit}, %{admin: %{PermissionEx.Test.Structs.Page => %{action: :show}}})
  false

  iex> PermissionEx.test_tagged_permissions(%PermissionEx.Test.Structs.Page{action: :show}, %{PermissionEx.Test.Structs.Page => true})
  true

  iex> PermissionEx.test_tagged_permissions(%PermissionEx.Test.Structs.Page{action: :show}, %{PermissionEx.Test.Structs.Page => %{}})
  false

  iex> PermissionEx.test_tagged_permissions(%PermissionEx.Test.Structs.Page{action: :show}, %{PermissionEx.Test.Structs.Page => %{}})
  false

  iex> PermissionEx.test_tagged_permissions(%PermissionEx.Test.Structs.Page{action: :show}, %{PermissionEx.Test.Structs.Page => %PermissionEx.Test.Structs.Page{action: :show}})
  true

  iex> PermissionEx.test_tagged_permissions(%PermissionEx.Test.Structs.Page{action: :show}, %{PermissionEx.Test.Structs.Page => %PermissionEx.Test.Structs.Page{action: :_}})
  true

  iex> PermissionEx.test_tagged_permissions(%PermissionEx.Test.Structs.Page{action: :show}, %{PermissionEx.Test.Structs.Page => %PermissionEx.Test.Structs.Page{action: nil}})
  false

  iex> PermissionEx.test_tagged_permissions(%PermissionEx.Test.Structs.Page{action: :show}, %{PermissionEx.Test.Structs.Page => [%PermissionEx.Test.Structs.Page{action: :show}]})
  true

  iex> PermissionEx.test_tagged_permissions(%PermissionEx.Test.Structs.Page{action: :show}, %{PermissionEx.Test.Structs.Page => [%PermissionEx.Test.Structs.Page{action: :_}]})
  true

  iex> PermissionEx.test_tagged_permissions(%PermissionEx.Test.Structs.Page{action: :show}, %{PermissionEx.Test.Structs.Page => [%PermissionEx.Test.Structs.Page{action: nil}]})
  false

  iex> PermissionEx.test_tagged_permissions(%PermissionEx.Test.Structs.PageReq{action: :show}, %{PermissionEx.Test.Structs.PageReq => [%PermissionEx.Test.Structs.PagePerm{action: :_}]})
  true

  iex> PermissionEx.test_tagged_permissions(%PermissionEx.Test.Structs.PageReq{action: :show}, %{PermissionEx.Test.Structs.PagePerm => [%PermissionEx.Test.Structs.PagePerm{action: :_}]})
  false