Heimdallr: ORM field-level security
We are currently migrating most of our products to browser-side application. One of the worst issues it raises is proper permissions handling. There are no comfortable ways to implement context-based protection of models (and their fields) within ActiveRecord (Egor, say hi ;).
attr_acessible is too weak. CanCan is too abstract (doesn’t go down to fields).
Let’s start from the deeper problem investigation though. Large part of Rails projects equates security to a REST restriction. The bigger projects sometimes fall down to a model to keep code DRY. And to keep your controllers/actions number from getting wild you may fall down to fields.
For properly designed RESTful applications, 1st and 2nd levels are same. So we are left with:
- Entity access level
- Entity fields separate restrictions
Field-level management gets more and more important while your application grows. And Github’s discreditation is a great example of what you can get if you go “fields? Who cares?..” way.
To take a long story short, here’s what
Heimdallr allows to define inside a model:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
Using straightforward DSL inside your models you define both, model and field-level restrictions.
Heimdallr will extend all required models with
.restrict method. It will wrap your model class into the Proxy that can be used in a default manner.
Note that an entity (second) parameter is not always available during evaluation. Therefore all the checks depending on inner fields state should be wrapped with
These restrictions can be used anywhere in your project. Not only in your controllers. And that’s damn important. If you try to get anything that is protected – you get an exception. This makes the behavior predictable. But it’s so uncomfortable for the views!
To avoid this
Heimdallr has two restriction strategies. By default it will follow the first one, explicit strategy that raises an exception. However this is how you can switch:
1 2 3 4 5
For the most Rails projects the Security term is often an alias for the CanCan gem. While CanCan was really an epoch and it still is superb it has some problems:
- CanCan was designed to interfere with models as least as possible. It proposes architecture where you get your REST implementation protected but models are plain and unrestricted. By itself this plan is sometimes good and sometimes not. The fact is that it can not get to fields whatever you do.
- 1.x branch is dead and unsupported. It has some awful bugs for complex cases with namespaces and 2.x takes so much time to appear.
Heimdallr as a tool to maintain security on a model level but it appeared that we have enough info to restrict controller among our DSL. So it took just a few moment to come up with
The resource part of
Heimdallr mimics CanCan as much as possible. You still get your
load_and_authorize filter and this is how it works:
- If you don’t have your :create scope defined (and therefore can not create any entity) you are considered to not be able to request new and create.
- If you don’t have your :update scope, you can not request edit and update.
- Same goes for :destroy scope.
- Inside your actions you get protected entities so you can’t forget explicit restrict call.
Here is the example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
REST API Providers
I’ve started my narrative from the roots of these gems, the restriction sync between client applications and server-side REST-based APIs. Let me tell you a bit about conventions we came up with.
Imagine you have simple role-based CRUD interface that you want to implement on a browser side. You have index/create/update/destroy REST endpoint. Restrictions give us following questions:
- Which entities am I able to get through index?
- Which entities of those are modifiable?
- Which entities of those are destroyable?
- Am I able to create a new entity?
- Which fields am I able to modify for one of those entities I’m able to edit?
- Which fields am I able to fill while creating a new entity if I’m able to?
The first question is already addressed by
Heimdallr itself. You get your scope and you simply can’t get anything besides what you are allowed to.
To get further with 2nd and 3d we should use meta-magic provided by
@model is supposed to be resricted. Add this fields to your serialization and you know the capabilities of current user.
Am I able to create? And which fields?
new method is a rare guest among REST APIs. And it’s a perfect place to determine if we are able to create entity and how exactly. Here is the code to list fields we can modify:
Heimdallr::Resource you’ll get restriction error if you can not create it at all.
Heimdallr either defines
.creatable? method so you can pass it on too.
Which fields am I able to update
The idea behind modification is quite the same. Just use
edit method and
:update keyword to retrieve fields that are accessible.
Heimdallr::Resource you can get your application protected quite well with no boilerplate. And what’s not really hot: you get amazing magic for your REST APIs. So use it and be happy. Remember, Homakov is watching you!