Skip to content
/ ruolo Public

A library to keep your static role-based access control policies in sync with your database

License

Notifications You must be signed in to change notification settings

mfinelli/ruolo

Repository files navigation

Ruolo

RubyGems Build Status Coverage Status Inline docs

A library for generating and keeping your static role-based access-control policies in sync with your database using the sequel gem.

You're an application developer and know all of the permissions and roles that you want to create - you want to create them in code and have them persist to the database (and stay in sync should you add/change/remove something). This is what ruolo can help with: define your RBAC policies using a DSL and run the sync on application start up. You're responsible for both the authentication and authorization of users after that, ruolo is completely unopinionated.

Usage

Require

Include the gem in your gemfile:

gem 'ruolo', require: false

Notice the require: false which is necessary so it doesn't automatically get included if you do something like Bundler.require. Make sure that you have a database connection and then require the gem and configure it.

Migrations

You'll need to run the following database migration to setup your schema, fill in the users table with your own details:

Sequel.migration do
  change do
    create_table :users do
      primary_key :id, type: :Bignum

      column :email, String, null: false, size: 190, unique: true
      column :password, String, null: false, size: 60, fixed: true

      column :first_name, String, null: false
      column :last_name, String, null: false

      column :created_at, DateTime, null: false
      column :updated_at, DateTime, null: false

      constraint(:valid_email, email: /@/)
    end

    create_table :roles do
      primary_key :id, type: :Bignum

      column :name, String, null: false, size: 150, unique: true

      column :created_at, DateTime, null: false
      column :updated_at, DateTime, null: false

      constraint(:valid_name, name: /^[A-Z]([A-Z0-9]*[-._]?)*$/)
    end

    create_table :permissions do
      primary_key :id, type: :Bignum

      column :name, String, null: false, size: 150, unique: true

      column :created_at, DateTime, null: false
      column :updated_at, DateTime, null: false

      constraint(:valid_name, name: /^[A-Z]([A-Z0-9]*[-._]?)*$/)
    end

    create_table :users_roles do
      foreign_key :user_id, :users, null: false, type: :Bignum,
        on_update: :cascade, on_delete: :cascade
      foreign_key :role_id, :roles, null: false, type: :Bignum,
        on_update: :cascade, on_delete: :cascade
      primary_key %i[user_id role_id]
      index %i[role_id user_id]
    end

    create_table :roles_permissions do
      foreign_key :role_id, :roles, null: false, type: :Bignum,
        on_update: :cascade, on_delete: :cascade
      foreign_key :permission_id, :permissions, null: false, type: :Bignum,
        on_update: :cascade, on_delete: :cascade
      primary_key %i[role_id permission_id]
      index %i[permission_id role_id]
    end
  end
end

User Class

You'll need to set your User class to use the correct association class, and configure ruolo to use the correct user class (you can also include the ruolo user module for the permission? mixin helper):

Ruolo.configure do |c|
  c.user_class = 'YourApp::User'
end

module YourApp
  class User < Sequel::Model
    include Ruolo::Models::User
    plugin :timestamps, update_on_create: true
    many_to_many :roles, join_table: :users_roles, class: 'Ruolo::Models::Role'
  end
end

Policy

To create your static permissions and roles create a yaml file with the following format, the permission list is derived from the permissions that are defined for the roles:

---
roles:
  POST_ADMIN:
    - CREATE_POST
    - DELETE_POST
  POST_CREATOR:
    - CREATE_POST

Sync

After adding the migrations, configuring ruolo and your custom user class, and defining your RBAC policy, run the sync during your application startup:

require 'sequel'
require 'ruolo'

DB = Sequel.connect('...')

Ruolo.configure do |config|
  config.connection = DB
end

Ruolo.synchronize!('./path/to/policy.yml')

Gem Development

To hack on the gem you'll need PostgreSQL installed and running, then create a user and database:

$ createuser ruolo
$ createdb ruolo

Then you can ensure everything was created correctly:

$ psql -U ruolo

You can use the bin/console command which will load in dependencies, and migrate the database on load. It also provides the helper methods reset_db! and migrate_db! if you want to run those operations manually after the code has loaded.

$ bundle exec bin/console

License

Copyright 2019-2023 Mario Finelli

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.