Advanced Roles talk from RailsSaaS 2022
At RailsSaaS 2022 I did a talk on strategies for implementing role based systems in Ruby on Rails applications. I discussed various ways to model permission systems and did a demo using the new Bullet Train Roles gem.
For the demo, I built an application using Bullet Train and showed three different ways to assign permissions to users.
- Assign different levels of permission to different members of the same team
- Elevate a user’s permission in the context of a single model
- Add an “Agency” team and allow the agency users to manage the Campaigns of their clients
After my talk, a number of people asked me if I could share the exact shell commands I ran on stage to build the demo application. In this post, I’ll go through each of the commands with a brief summary of what it does. When the RailsSaaS videos are released, I’ll update this page with a link to my talk so you can see the full context for each of these commands. In the mean time, this article serves as a reference for anyone who was at the conference and wanted to experiment with the different approaches I used in the demo.
Initial Setup
These first commands I ran ahead of time and didn’t actually do in the demo:
- Clone the started repo
- Run the setup script
git clone git@github.com:bullet-train-co/bullet_train.git ad-vantage
cd ad-vantage
bin/setup
Follow the prompts in the setup script and you should have a working starter application. You can start the server and test it out by running:
bin/dev
Creating the Application Models
Once you have the Bullet Train starter app running on your machine, you should be ready to dive into the commands from the talk.
First up, I added two basic models to the application. These models served as placeholders for “real” parts of the application. I added a Campaign
model and a Payment
model. I used the rails generator to create the model then ran the Bullet Train super-scaffolding
command to build all the views.
bin/rails g model Campaign team:references name:string
bin/super-scaffold crud Campaign Team name:text_field
bin/rails g model Payment team:references name:string amount:float
bin/super-scaffold crud Payment Team name:text_field amount:text_field
You can learn more about the super-scaffolding
command in the Bullet Train docs
Migrate the database and you should now be able to interact with the models we just created.
bin/rails db:migrate
Bullet Train ships with a default implementation of the Roles gem that meets the requirements of the first version of our permission system so there’s no additional commands to run here. In the talk, I removed access to the Payment
model completely for the default user - I’m leaving this as an exercise for the reader.
Creating a Campaign Collaborator
Next up, we want to make a default user an admin of a single campaign - aka “Escalating the permissions of a user in the context of a single model”.
bin/rails g model Campaigns::Collaborator campaign:references user:references role_ids:jsonb
bin/super-scaffold crud Campaigns::Collaborator Campaign,Team campaign_id:super_select{class_name=Campaign} user_id:super_select{class_name=User} role_ids:super_select{vanilla}
bin/rails db:migrate
Creating an Agency Team
Finally, we create an association from one team to another as a way to implement our Agency team.
bin/rails g model Client team:references client_team:references role_ids:jsonb
bin/super-scaffold crud Client Team team_id:super_select{class_name=Team} client_team_id:super_select{class_name=Team} role_ids:super_select{vanilla}
bin/rails db:migrate
Wrap Up
You can see the finished state of the code from my talk here:
https://github.com/adampal/advanced-roles-talk
Please note that as this was a demo, I didn’t attempt to make the app complete. You will hit errors with missing translations and other small UI problems. For example, in the final version where I added the Agency team, I added an index view to show that the agency did in fact have access to their clients Campaigns. However, if the agency attempts to get to the show page of any of those campaigns, they will hit an error because the show page was only scaffolded and designed for the client side of things. In a real application, you would still need to solve these issues.
If you experiment with any of these, please let me know how you go. What are your thoughts on each of the setups and what would you do differently?
My twitter DMs are always open.