Praetorian is a minimalist Crystal authorization system inspired by Pundit. It aims to be both lightweight and dependency-less.
dependencies: praetorian: github: ilanusse/praetorian
Praetorian, inspired by Pundit, works with policy classes. This shard is not designed to be extra compatible with any framework but rather with flexibility in mind. This is a simple example that allows updating a post if the user is an admin, or if the post is unpublished:
class Post def policy_class PostPolicy end end class PostPolicy include Praetorian::Policy property user, post def initialize(user, post) @user = user @post = post end def update? user.admin? || !post.published? end end # Somewhere in your code def update @post = Post.find(params[:id]) Praetorian.authorize(current_user, @post, :update?) # You can also use .authorise if you're a Brit # Rest of code flow end
There are two things to notice here:
The Post is a class that should obey a certain Policy. We can either write a
policy_class method to return the policy class name, or Praetorian will assume the policy classname to be
The Policy class includes
Praetorian::Policy. This adds default query methods to our policy as defaults that should be overwritten as necessary.
The default query methods defined in
Praetorian::NotAuthorizedException will be raised if the user is not authorized to perform said query on the record.
Ok. So far, pretty simple.
You can set up a simple base class to inherit from:
class ApplicationPolicy include Praetorian::Policy property user, object def initialize(user, object) @user = user @object = object end end
You can include the shard as a module in your controller base class to avoid the prefix:
class ApplicationController include Praetorian end class PostController < ApplicationController @post = Post.find(params[:id]) authorize(current_user, @post, :update?) # yay no prefix end
You can pass an argument to override the policy class if necessary. For example:
def create @publication = find_publication # assume this method returns any model that behaves like a publication # @publication.class => Post Praetorian.authorize(current_user, @publication, :create?, PublicationPolicy) # Rest of code flow end
Licensed under the MIT license, see the separate LICENSE.txt file.