Skip to main content
Version: v3.0

Creating a custom role

This tutorial will walk you through adding a custom author role to your application.

Prerequisites​

Ensure you have completed the Organization and Roles Tutorial

Data model​

The data model for this tutorial is the same as the Organization and Roles Tutorial

Generate a new policy​

docker compose run --rm backend "rails generate rhino:policy Author"
info

rails generate is a built in Rails command that will run a generator for you. In this case we are generating a new policy called "author". There are a number of generators available in Rails & Rhino. You can see a list of them by running rails generate without any arguments.

There will now be a new files, app/policies/author_policy.rb with the skeleton of a policy and test/policies/author_policy_test.rb with the skeleton of a policy test.

Edit the policy​

app/policies/author_policy.rb
class AuthorPolicy < ::Rhino::ViewerPolicy
def check_author
return false unless auth_owner

# Check if the user is the author of the blog
record.class.where(id: record.id).joins(record.joins_for(:blog)).exists?("blogs.user_id": auth_owner.id)
end

# Can always create (for themselves)
def create?
authorize_action(true)
end

# Can only update if they are the author
def update?
authorize_action(check_author)
end

# Can only destroy if they are the author
def destroy?
authorize_action(check_author)
end

class Scope < ::Rhino::ViewerPolicy::Scope
end
end

Seed the database​

Create an additional organization and role for testing.

db/seeds/development/organizations.rb
require_relative "users"

user = User.find_by(email: "test@example.com")
user_other = User.find_by(email: "other@example.com")

# Create sample organizations
org = ["Single User Org", "Multi User Org", "Viewer Org", "Editor Org", "Author Org"].map do |name|
Organization.find_or_create_by!(name:)
end

role_admin = Role.find_or_create_by!(name: "admin")
role_viewer = Role.find_or_create_by!(name: "viewer")
role_editor = Role.find_or_create_by!(name: "editor")
role_author = Role.find_or_create_by!(name: "author")

# The test@example.com user is the only user and admin of the "Single User Org" organization
UsersRole.find_or_create_by!(user:, organization: org[0], role: role_admin)

# The test@example.com and other@example.com are both users and admins of the "Multi User Org" organization
UsersRole.find_or_create_by!(user:, organization: org[1], role: role_admin)

# The test@example.com is a viewer and other@example.com is an admin of the "Viewer Org" organization
UsersRole.find_or_create_by!(user:, organization: org[2], role: role_viewer)
UsersRole.find_or_create_by!(user: user_other, organization: org[2], role: role_admin)

# The test@example.com is an editor and other@example.com is an admin of the "Editor Org" organization
UsersRole.find_or_create_by!(user:, organization: org[3], role: role_editor)
UsersRole.find_or_create_by!(user: user_other, organization: org[3], role: role_admin)

# The test@example.com is an author and other@example.com is an admin of the "Author Org" organization
UsersRole.create!(user: user, organization: org[4], role: role_author)
UsersRole.create!(user: user_other, organization: org[4], role: role_admin)
db/seeds/development/blogs.rb
require_relative "organizations"

user = User.find_by(email: "test@example.com")
user_other = User.find_by(email: "other@example.com")

# Function to generate sample blogs and blog posts
def generate_blogs(user, org)
5.times do
blog = Blog.create!(user_id: user.id, organization: org, title: FFaker::Book.unique.author, category_id: Category.ids.sample)
20.times do
BlogPost.create!(blog_id: blog.id, title: FFaker::Book.unique.title, body: FFaker::Book.unique.description, published: [true, false].sample)
end
end
end

# Only test@example.com authors blogs in the "Single User Org" organization
org = Organization.find_by(name: "Single User Org")
generate_blogs(user, org)

# Both users author blogs in the "Multi User Org" organization
org = Organization.find_by(name: "Multi User Org")
generate_blogs(user, org)
generate_blogs(user_other, org)

# Only other@example.com authors blogs in the "Viewer Org" organization
org = Organization.find_by(name: "Viewer Org")
generate_blogs(user_other, org)

# Only other@example.com authors blogs in the "Editor Org" organization
org = Organization.find_by(name: "Editor Org")
generate_blogs(user_other, org)

# Both test@example.com author blogs in the "Author Org" organization
org = Organization.find_by(name: "Author Org")
generate_blogs(user, org)
generate_blogs(user_other, org)

And add the seed data to the database

docker compose run --rm backend "rails db:seed"

Restart the server​

docker compose restart backend

Try the Author Org​

Login to the application with test@example.com and password and switch to the "Author Org" organization. Viewing the blogs where the author is test@example.com will allow editing and deleting:

While blogs where the author is not test@example.com will not: