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).
We’ve figured out something awesome to solve this issue. Meet Heimdallr and it’s extension Heimdallr::Resource. They will bring you a peace and security.
Heimdallr
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.
1
|
|
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 .try(:field)
.
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 |
|
CanCan
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.
We’ve started 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 Heimdallr::Resource
.
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 Heimdallr
proxy:
1
|
|
@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:
1
|
|
Within 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.
1
|
|
Summary
Using Heimdallr
and 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!
ಠ_ಠ